PHP 5.4.1の新機能: Scalaライクなミックスイン

2012年4月1日にリリースされたPHP 5.4.1では、マイナーバージョンアップにも関わらず言語仕様が拡張されるという、PHPクォリティ全開の変更があったので紹介しよう。


実体化時ミックスイン (Mix-in instantiation)

クラスをインスタンス化する際にuse文でトレイトを追加できるようになった。トレイトは実行時に解決されるので、クラス宣言より後にトレイトを宣言することも可能。

例1

<?php
class JoJo {}

trait HermitPurple {
    public function sayStand() {
        echo '隠者の紫';
    }
}

trait StarPlatinum {
    public function sayStand() {
        echo '星の白金';
    }
}

$joseph = new JoJo use HermitPurple;
$jotaro = new JoJo use StarPlatinum;

$joseph->sayStand();
echo '/';
$jotaro->sayStand();

出力1

隠者の紫/星の白金

当然オートボクシングもできる。

例2

<?php
trait Hello {
    public function sayHello() {
        echo 'Hello';
    }
}

(new stdClass use Hello)->sayHello();

出力2

Hello

衝突の解決

new-use構文によるミックスインでもinsteadof演算子やas演算子によって衝突の解決や別名の定義が可能。

例3

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

$talker = new stdClass use A, B {
    B::smallTalk insteadof A;
    A::bigTalk insteadof B;
    B::bigTalk as talk;
};
$talker->smallTalk();
echo ',';
$talker->bigTalk();
echo ',';
$talker->talk();

出力3

b,A,B

無名トレイト (Anonymous Traits)

実体化時ミックスインでは、use文のブレース中にメソッドおよびプロパティを記述できる。厳密にはトレイトでないのだが、便宜上この機能を無名トレイト (Anonymous Traits)と呼ぶ。

例4

<?php
(new stdClass use {
    public function sayHello() {
        echo 'Hello';
    }
})->sayHello();

出力4

Hello

名前付きトレイトとの併用

無名トレイトは通常の名前付きトレイトと併用することもできる。

例5

<?php
class JoJo {}

trait GoldExperience {
    public function sayStand() {
        echo 'ゴールド・エクスペリエンス';
    }
}

trait Requiem {
    public function sayStand() {
        echo 'レクイエム';
    }
}

$giorno = new JoJo use GoldExperience, Requiem {
    GoldExperience::sayStand as sayStand1;
    Requiem::sayStand as sayStand2;

    public function sayStand() {
        $this->sayStand1();
        echo '・';
        $this->sayStand2();
    }
};

$giorno->sayStand();

出力5

ゴールド・エクスペリエンス・レクイエム

implements演算子による機能チェック

PHPのトレイトは型システムとしての機能を備えていないので、instanceof演算子でトレイトが追加されているか調べることはできない。しかしミックスインによってインスタンス毎にトレイトを追加できるので、instanceof演算子だけではインスタンスが持つ機能を判定できなくなった。そこで、implements演算子を用いてトレイトの機能をチェックする。

例6

<?php
class Dio {}

trait TheWorld {
    public function sayHello() {
        echo 'WRYYY!';
    }
}

$dio = new Dio use TheWorld;
var_dump($dio implements TheWorld);
$dio->sayHello();

出力6

bool(true)
WRYYY!

注意事項

implements演算子による機能チェックは Class implements Interface によるクラス宣言時と同等のシグネチャ検証をする。そのため、右辺のトレイトが提供する機能を備えていれば、そのトレイトが追加されていなくても真となる。

例7

<?php
class Dio {}

trait TheWorld {
    public function sayHello() {
        echo 'WRYYY!';
    }
}

trait ScaryMonsters {
    public function sayWorld() {
        echo 'WRYYYYYY!';
    }
}

$dio = new Dio use TheWorld { TheWorld::sayHello as sayWorld };
var_dump($dio implements TheWorld);
var_dump($dio implements ScaryMonsters);
$dio->sayWorld();

出力7

bool(false)
bool(true)
WRYYY!