LoginSignup
4
4

More than 5 years have passed since last update.

PHP trait の罠

Last updated at Posted at 2015-05-13

罠ってほどでもない

例えば下のようなコードがあったとします。

<?php
trait A
{
    public function hoge()
    {
        echo 'hoge' . PHP_EOL;
    }

    public function fuga()
    {
        echo 'fuga' . PHP_EOL;
    }
}

trait B
{
    public function hoge()
    {
        echo 'hogeB' . PHP_EOL;
    }

    public function fuga()
    {
        $this->hoge();
    }
}

class C
{
    use A, B {
        A::hoge insteadof B;
        A::fuga insteadof B;
        B::hoge as hoge2;
        B::fuga as fuga2;
    }

    public function main()
    {
        $this->hoge();
        $this->fuga();
        $this->hoge2();
        $this->fuga2();
    }
}

$instance = new C();
$instance->main();

要するに、同じメソッド名を持つトレイトをクラスに取り込んだ上で、Aのメソッドを優先し、Bのメソッドにはエイリアスをつける、というようなコードです。
さて、上記のコードを実行すると何が出力されるでしょう?

答え

% php test.php
hoge
fuga
hogeB
hoge

どういうことだってばよ…

つまり、C::main()$this->fuga2() を呼んだとき、B::fuga2() の中身は「B::hoge() を実行せよ」という意味ではなく「C::hoge()(つまり A::hoge())を実行せよ」と解釈されるため、B::hoge() が呼ばれることはない、ということなんですねー。

応用

以下の様な感じでインターフェイスとトレイトを使えばこんなことも。

<?php
interface A
{
    public function hoge();
    public function fuga();
    public function piyo();
}

trait B
{
    public function hoge()
    {
        return 'hoge';
    }
}

trait C
{
    public function fuga()
    {
        echo $this->hoge() . PHP_EOL;
        echo $this->piyo() . PHP_EOL;
    }
}

class D implements A
{
    use B;
    use C;

    public function main()
    {
        $this->fuga();
    }

    public function piyo()
    {
        return 'piyo';
    }
}

$instance = new D();
$instance->main();
% php test.php
hoge
piyo
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4