Perl日記

PerlとかRubyとかPHPとかPythonとか

PHPでTraitのメソッド名をasで変更してuseして、うまいことする

PHPのTraitはuseしたクラスでTraitと同じメソッド名のメソッドを定義すると、それで上書きしてしまう。

<?php
trait MyTrait {
    public function foo() {
        echo "MyTrait::foo()~~~\n";
    }
}

class Hoge {
    use MyTrait;
    public function foo() { // 上書きしてしまう
        echo "Hoge::foo()======\n";
    }
}

(new Hoge)->foo();

//=> Hoge::foo()======

Traitは継承関係に組み込まれるわけではないので、parentで呼ぶこともできない。

<?php
trait MyTrait {
...
}

class Hoge {
    use MyTrait;
    public function foo() {
        parent::foo(); // parentで呼んでみる
        echo "Hoge::foo()======\n";
    }
}
PHP Fatal error:  Cannot access parent:: when current class scope has no parent

こんなときは、useするときに、asで別名にして差し込めばいいみたい。

<?php
trait MyTrait {
...
}

class Hoge {
    use MyTrait { foo as bar; } // Traitのfooの名前をbarにする

    public function foo() {
        $this->bar(); // barで呼ぶ
        echo "Hoge::foo()======\n";
    }
}


//=> MyTrait::foo()~~~
//=> Hoge::foo()======
親クラス、子クラス、Traitの同名メソッドを全部呼ぶ

これを利用して(というか使わざるを得ない)、継承とTraitのメソッドを全部たどって呼ぶ方法。

<?php
trait HogeTrait {

    // Traitの h() メソッド
    public function h() {
        $res = parent::h(); // ParentHoge::h()を呼ぶ

        echo "HogeTrait::h() dayo\n";

        $res['trait'] = 'traitdayo';

        return $res;
    }
}

class ParentHoge {

    // 親クラスの h() メソッド
    public function h() {
        echo "ParentHoge::h() dayo\n";

        return ['parent' => 'parentdayo'];
    }
}

class ChildHoge extends ParentHoge {
    use HogeTrait { h as hh; } // 別名で上書きを防ぐ

    // 子クラスの h() メソッド
    public function h() {
        $res = $this->hh();

        echo "ChildHoge::h() dayo\n";

        return $res;
    }
}

$res = (new ChildHoge())->h();
var_dump($res);



//=> ParentHoge::h() dayo
//=> HogeTrait::h() dayo
//=> ChildHoge::h() dayo
//=> array(2) {
//=>   'parent' =>
//=>   string(10) "parentdayo"
//=>   'trait' =>
//=>   string(9) "traitdayo"
//=> }

ちなみにRuby

RubyのMixinは親子関係に組み込まれるので、ModuleをincludeしたクラスでsuperでModuleのメソッドが呼べる。

module HogeModule
  def h
    res = super
    puts "HogeTrait::h() dayo"
    res[:module] = 'moduledayo'
    res
  end
end

class ParentHoge
  def h
    puts "ParentHoge::h() dayo"
    {parent: 'parentdayo'}
  end
end

class ChildHoge < ParentHoge
  include HogeModule
  def h
    res = super
    puts "ChildHoge::h() dayo"
    res
  end
end

h = ChildHoge.new
p h.h


#=> ParentHoge::h() dayo
#=> HogeTrait::h() dayo
#=> ChildHoge::h() dayo
#=> {:parent=>"parentdayo", :module=>"moduledayo"}