PHPでAOPしてみた
PHPでDIはたまに見かけるけどPHPでAOPってのはあまりみないですね。S2.PHP5くらいしかない。
で、AOPしたいってだけでS2.PHP5をいれるのもアリでしょうけど、もっとちっちゃく利用したかったのでプロトタイプを作ってみた。
アスペクトクラスを書く
<?php class Aspect { private $clazz; private $obj; private $intercepters = array(); public function __construct($obj) { if (is_null($obj) || !isset($obj) || !is_object($obj)) { throw new Exception('オブジェクトじゃないとだめ'); } $this->obj = $obj; $this->clazz = new ReflectionClass($obj); } public function pointcut($joinpoint, $intercepter) { if (!$this->clazz->hasMethod($joinpoint)) { throw new Exception('ジョインポイントなし'); } $this->intercepters[$joinpoint] = $intercepter; } public function run($joinpoint, $args = array()) { if (!array_key_exists($joinpoint, $this->intercepters)) { throw new Exception('ポイントカットされてない'); } $intercepter = new $this->intercepters[$joinpoint]; $intercepter->invoke($this->obj, $this->clazz->getMethod($joinpoint), $args); } }
インターセプターを書く
<?php interface Intercepter { public function invoke($object, ReflectionMethod $method, array $args = null); } class TestIntercepter implements Intercepter { public function invoke($object, ReflectionMethod $method, array $args = null) { echo 'before</br>'; $ret = $method->invoke($object, $args); echo '</br>'; echo 'after'; return $ret; } }
使い方
<?php class Test { public function testAspect() { echo 'aspect'; } } $aspect = new Aspect(new Test()); $aspect->pointcut('testAspect', 'TestIntercepter'); $aspect->run('testAspect');
結果
before aspect after
まとめ
もちろんこのままで使い物になりませんが、基本としてこんな感じでよいかと。上記テストクラスのファンクションにアノテーションでポイントカットとインターセプター、あとタイミング(呼び出される前とか後とか両方とか例外が発生した時とか)が指定できるとかなり使いやすくなるんじゃないかと。近いうちに作ってみようかと思います。
DIとかAOPについて思うこと
最近PHPフレームワークでもDIを実装しようとする動きがあるみたいですね。symfonyとか。で、思うんですがユニットテストに重きを置いていないプロジェクトの場合、それほどDIの恩恵を受けることができないと思います。つか多分いらない。あ、大きなアプリには役に立つかもしれません。まだできてない部分にモックコンポーネントを作ってコンテナの設定1つで置き換えるとか。
で、AOPは使い方が限られますが定番の使い方っていうのがあって(ログとったりとかトランザクションとかログインチェックとか)、DIよりわかりやすく使える。とくにどうという結論はありませんが、僕はこう思っていますということで。