php5.3 以前で mixin 的なものを実装
php5.4 以降であれば Trait を使って mixin 的なことは実現できますが(※)、 php5.3 以前で mixin したい場合はどうするか。
いろいろ考えましたが、一番シンプルで使いやすいかなーという方法を紹介します。
※mixin と trait の違いとかよう分かってへんけどこまけぇこたぁいいんだよ!(AA略)
ここが素敵
- インスタンスメソッドも静的メソッドも mixin 出来ます(ただし静的メソッドの mixin は php5.3.0 以降でのみ可能)。
- もちろん mixin したメソッドに引数を渡すことも出来ます。
- mixin したインスタンスメソッドの中から、対象クラスの public なメンバ変数にアクセス出来ます。
- 複数の mixin クラスを同時に mixin 出来ます。
まずはコード
サンプルコードです。
Trait が導入される php5.4 より前の php で mixin 的なことを実装してみた。
これを実行するとこう。
$ php mixin.php // $foo->mixin_instance_method これは MixinClass のインスタンスメソッドです yahoooooo! 2013-10-11 Foo の public メンバ変数にアクセスすることも出来ます。 999 // $foo->mixin2_instance_method これは MixinClass2 のインスタンスメソッドです // $foo->instance_method これは Foo 自身のインスタンスメソッドです // $foo->parent_instance_method これは親クラス Bar のインスタンスメソッドです // Foo::mixin_static_method これは MixinClass の静的メソッドです yahoooooo! 2013-10-11 // Foo::mixin2_static_method これは MixinClass2 の静的メソッドです // Foo::static_method これは Foo 自身の静的メソッドです // Foo::parent_static_method これは親クラス Bar の静的メソッドです * * * + n ∧_∧ n IINE! + (ヨ(* ´∀`)E) Y Y *
いいねー。
解説
では解説です。インスタンスメソッド版 mixin と静的メソッド版 mixin の2つに分けて説明します。
1. インスタンスメソッド版 mixin
さっきのコードから、インスタンスメソッドの mixin に関わるとこだけ切り出すとこう。
1-1. mixin クラスを作成
まずは mixin クラスを作りましょう。
ほぼ、普通にクラス定義するだけですけど一点注意。 インスタンスメソッドの第1引数は $that とか $self とかの名前にして下さい。 ここに mixin 対象クラスのインスタンスが渡されてきます。これを通じて mixin 対象クラスの public なメンバ変数にアクセス出来るようになります。
// このように呼ぶメソッドであれば、 $foo->message("hello."); // 定義はこうではなく、 public function message($message) { /* (´・ω・`) */ } // こうです。 public function message($that, $message) { /* (・∀・)イイネ!! */ } // 引数なしで呼ぶメソッドであっても、 $foo->bar(); // 定義はこうではなく、 public function bar() { /* (´・ω・`) */ } // こうです。 public function bar($that) { /* (・∀・)イイネ!! */ }
1-2. mixin 対象クラスを修正
mixin 対象クラスをいじって __construct と __call をオーバーライドします。
__construct で MixinClass のインスタンスを生成してメンバ変数に持っておきます。
public function __construct() { $this->mixin1 = new MixinClass(); }
__call では、渡されてきたメソッド名 $method_name が MixinClass のインスタンスメソッドとして実行可能か調べて、そうあれば実行。 実行する際、第1引数に $this を渡します。これがさっき mixin クラス側でメソッドの第1引数とした $that (or $self) です。
public function __call($method_name, $args) { // Foo 自身を表す $this を引数リストの先頭に追加します array_unshift($args, $this); // $this->mixin1->$method_name() が実行可能か調べる if (is_callable(array(&$this->mixin1, $method_name))) { // MixinClass#$method_name を実行 return call_user_func_array(array(&$this->mixin1, $method_name), $args); } else { // そんなインスタンスメソッドはどこにも見つかりませんでした。。。 throw new BadMethodCallException(); } }
2. 静的メソッド版 mixin
静的メソッドの mixin に関わるとこだけ切り出すとこう。
2-1. mixin クラスを作成
静的メソッドを持つクラスをふつーに定義するだけです。
2-2. mixin 対象クラスを修正
mixin 対象クラスでは __callStatic をオーバーライドします。MixinClass::$method_name が実行可能かどうか調べて、あれば実行。
public static function __callStatic($method_name, $args) { if (is_callable("MixinClass::$method_name")) { // MixinClass::$method_name を実行 return call_user_func_array("MixinClass::$method_name", $args); } else { // そんな静的メソッドはどこにも見つかりませんでした。。。 throw new BadMethodCallException(); } }
おしまい。
お解り頂けたかしら_(:3」∠)_