EA1では何がおきているか
さて、EA1でなにをしているかというと次のようなことをしています。
- dicon.iniに書かれているコンポーネント定義をみる
- コンポーネント識別子(foo.bar.zooのようなもの)をみて、使用するファイルを特定する。ただし、ここで今まで「foo.bar.zoo」であれば「components/foo/bar/Foo_Bar_Zoo.class.php」で「Foo_Bar_Zoo」クラスでないといけなかったが、「components/foo/bar/Zoo.class.php」で「Zoo」クラスでもいいようにした。
- (2)で特定したファイルをAspectをかませるように書き換えて、「components_c」に出力する。この際、「Zoo」クラスとなっている場合には「Foo_Bar_Zoo」に書き換えてしまう。
- (3)で出力したファイルをincludeしてオブジェクトを生成し、DIContainerに登録する。
- dicon.ini に「xxx = advice:yyy」という記述があれば、そのクラスの「xxx」というパターンに当てはまるメソッドに「yyy」という別名でDIContainerに登録されているコンポーネントをinterceptorとして適用する。
(3)は「components_c/」にまだファイルが出力されていないか、「components/」の方がファイルが新しい場合にだけ実行されるようになっています。
(5)に関して、interceptorを適用するメソッド名(上の説明では「xxx」にあたる部分)には正規表現を使えるようにしています。なので次のような記述ができます。
[TraceInterceptor:maple.interceptor.traceInterceptor] [addDto:aopexample.dto.addDto] /^a.*/ = advice:TraceInterceptor
これだと、「aで始まるメソッドにTraceInterceptorをかける」ってことになります。直接「add」というメソッドにかける場合は「/add/」ではなくて「add」とかけます(と説明をしていて、「add」とかけば「/^add$/」となった方がいいですね・・・次の修正でそうします・・・)
かなり気軽にcomponentに対してAspectをかけれます。EA1ではExecutionTimeInterceptorも合わせてリリースしているので、それらをかけたりはずしたりして、ログ(webapp/logs/maple.log)をみてもらえると動きが変わっていることがわかると思います(webapp/modules/aopexample/dicon.ini にいくつかコメントアウトをつくってますのではずしたりつけたりしてみてください)。
さて肝は(3)なわけです。昨日「極悪」と表現したのはこれで、元のクラスのメソッドの先頭に「_maple_」というのをつけて逃がし、元のメソッド名の部分は次の例のようなものを叩き込んでいます。次の例は待避した元のメソッド(_maple_setA)と生成したメソッド(setA)です。
function _maple_setA($a) { $this->a = $a; } function setA($a) { $methodArgs = array($a); $invocation =& new MethodInvocation($this, "setA", $methodArgs); $result = AspectManager::invoke($invocation); return $result; }
いろんなパターンの元クラスをつくって試してみたつもりではあるのですが、あたりがでる可能性はちょっと否定はできないですね・・・結構がんばっているつもりではあるのですが。
(2)に関しては、以前からちょくちょく出していた「擬似名前空間」です。これによりディレクトリ構成に合わせてクラス名が変わって他のクラスとはバッティングしないということを実現しています。(ようやく実現できてほっとしてたり・・・)
今回のはあくまでもこんなのどうでしょう!というものです。これをベースにMLで議論ができるといいなと思います。(なにせあまりにもcoreとの統合がいい加減すぎるので・・・)