とりあえず、getInstanceでインスタンス毎に分離できる形にして Xhwlay_Hook, TestCaseまで一通り終了。以降はいよいよ心臓部であるRunnerを作り直す。恐らくAbstractは消えて、Xhwlay_Runner_Baseみたくリネームされるのではないか?Invokerをとりあえず実装しちゃう。これによりAbstractな箇所が多分、無くなるはずだから。 あと重要な意志決定をいくつか。 - HOOKのInvoke順なんだけど、結局 First-Pushed, First-Called に固定した。 - HOOK中から動的に実行中のHook-Stackのpush/pop機能は、少なくとも現時点では実装しないことにした。 - ErrorStackを見てのHookチェインの自動停止は実装しないことにした。(escape()の手動callで一本化) * Invoke順を First-Pushed, First-Called 固定 実際の使い方を考慮すると、pushする順とはすなわち、設定ファイルなどに記述する順となる場合が圧倒的多数だろう。 例えばyamlファイルに以下のように記述したhookを実行するような実装をdeveloperが作ったとして、実行順序がこの逆になるような仕様が果たして分かりやすく使いやすいと言えるだろうか?直観的で誤解を招かないと言えるだろうか? - hooks: - user_hook: - hook1 - hook2 - hook3 こうした場合、もっとも自然な実行順序は設定順、すなわち先にpushした順であるのは殆どの人が同意するはずである。 もう一つの消極的理由としては、実行順序を将来的に変更するようになった場合、後方互換性を確保する為、恐らくattributeに順序を設定するのでは無かろうか?そうなると、恐らくinvoke()やその周りのインターフェイスを変える必要はなくなり、単にsetAttributeすれば良いだけになるだろう。 となると、現行のテストケースがそのまま使える。 変更箇所はおそらくinvoke()の内部だけに留められる筈であろう。であるならば、本当に必要になったその時に実装を考えても、今のところは困らないと考えた。 他に考慮したケースは、システム側でpushしておいたHookに、developerコードが追加でpushしたい場合。 class hoge_fw_runner { function init() { $h =& Xhwlay_Hook('init'); $h->pushCallback(array(&$this, "start_session")); $h->pushCallback(array(&$this, "restore_user_context")); } function run() { $h =& Xhwlay_Hook('init'); $h->invoke(); ... } } 上記のようなクラスを用い、例えば次のように実行する場合。 この場合、run()メソッドを呼ぶ前に "init"HOOKにdeveloper-hookをpushできるか?させるべきか?を考える。 安直なプログラマーであれば、run()する前に自分で"ini"HOOKにpushしてしまうかも知れない。 $h =& Xhwlay_Hook("init"); $h->pushCallback(...); hoge_fw_runner::run(); しかしこれでは、hoge_fw_runner::init()中の設定と干渉する。 もしinvoke順を、First-Push, Last-Calledにしておく、あるいは選択できるのであればこれに対応できる。 しかし、果たしてそうするべきだろうか?init()の中では実際に、First-Push, First-Callを期待して、先にstart_sessionが呼ばれるように調整してあるというのに!! $h->pushCallback(array(&$this, "start_session")); $h->pushCallback(array(&$this, "restore_user_context")); こうした場合、一番王道な方法はhoge_fw_runner自体を派生し、init()をオーバーライドするべきだろう。 pushCallback('your_own_hooks'); } /* * エントリポイント用の各種設定コード・処理 */ custom_fw_runner::run(); ?> どうしても"start_session"の前にオリジナルのHOOKを入れたいのであれば、親クラスのinit()を呼ぶ前に入れればよい。 function init() { $h =& Xhwlay_Hook("init"); $h->pushCallback('your_own_hooks_1'); hoge_fw_runner::init(); // PHP5なら、parent::init() で済む? $h->pushCallback('your_own_hooks_2'); } このような考慮の結果、特にinvoke順は First-Push, FirstCallでも問題ないとの結論に達した。 * HOOK中からの動的なHOOK-Chainの操作の保留 まずコードと仕様が複雑になる。escape()と組み合わされた場合も考慮すると、いささかややこしい。 で、その複雑なコードとややこしさが、果たして実際の実装要求と等価か?と考えると、不安。 つまり、現状まだそこまで使うようなシーンが思い浮かばないのだ。「できたらいいな」ではあるが、しかしその実体は「理論的に可能な遊び」に近い。従って、まだ本気で取り上げるべきでもない。 一応その下準備として、invoke()中では一旦hook-callbackのstack配列をコピーし、そのコピーでもってinvokeチェインを回している。 $copy_hook_stacks = $this->_callbacks; foreach ( $copy_hook_stacks as $callback) { ... } みたいな感じ。これにより、万一HOOK中でpush/popCallbackが行われても影響(被害?)を押さえられる。回しているのはコピーされたHOOKだからだ。 もしも本気で初期の構想で使いたい場合は、HOOK中からオリジナルのHOOKを呼び、escape()するのが良いかも知れない。 * ErrorStackを見てのHookチェインの自動停止は実装しないことにした。 これは構想のミス。当初はオリジナルのパッケージで挙げられたErrorStackを検出しようとしたが、Xhwlay_ErrorStackではパッケージ名でエラーを取得できなかった・・・のを、忘れてた。つまり、Xhwlay_ErrorStackでは不可能。 なので、escape()に一本化することにした。まあ、Invoking中のHOOK-Chainを止めるのはdeveloperの明確な意志に基づく、ということで。 * うっかり寝過ごしてもう朝の9時半だよ、間に合わないよ。 と言うことで午前半休にして、Xhwlay_Runnerに取りかかる。 とりあえず、RendererにstdClassでaci, pageName, bookmarkContainerId割り振っていたけど、いろいろ考えて、止める。 代わりにXhwlay_Varに XHWLAY_VAR_NAMESPACE_VIEW を追加して、これにセットするようRunnerを修正。それに伴い、AbstractRendererと、唯一getRequest()使っていたIncludeを修正し、SimpleTestまで終了。 あと、Rendererでsetup()やterminate()はHOOK実行に実装。また、setup()後にaci/pageName/BookmarkContainerId取ってるところを、ホワイトリストで文字種チェックをかけるよう、 _cleanup_outparam()というメソッドを追加してそれを通すよう修正。 また、これらのアウトパラムの最大文字数をRunnerに定義し、_cleanup_outparamでは "/ ... /m"で複数行対応でチェックするよう実装。 ただし_cleanup_outparamは未テスト。 invokePage, Barrier系は実装する旨TODOで書き付けておく。 他、AbstractRunnerは Xhwlay_Runnerにリネームし、ディレクトリを一つ上に上げた。Testも追随。&br; 但し、まだ古い方は削除するのちょっと待ち。 そんな感じ。