* Xhwlayの読み方 - 「ズーレイ」("ズー"にアクセント) - 「ズレイ」("レイ"にアクセント) 英語だと後者になるようですが、元々とある漫画の「ブーレイ騎士団」という名前が頭をかすめた時に思いついた名前ですので、前者を推したいところです。 ・・・が、2008年12月現在は作者自身が後者の"レイ"にアクセントを置いた方で読んでます。 * 概要 Piece Framework を強く意識し、ステートフルな画面遷移を行う為の、シンプルなコントローラです。フレームワークではありません。Piece Frameworkを意識していますが、ステート保持のコンセプトはFSM(Finite State Machine)ではなく、ページの流れを一つの「本」(Book)と捉え、ユーザーに現在表示するべきページを「栞」(Bookmark)として保持するような仕掛けにしています。従ってActiveStateやViewStateなどの区分けは存在しません。従来通りの「ページ - アクション」マッピングの思想を引き継いでいます。一応、Guardに相当する「Barrier」(バリア)を設定することが可能です。ページ遷移を特定条件でブロックするときなどに使います((例えば入力値のvalidationなど))。 例によりValidatorやORM、さらにロギングなどの連携は一切手を出していません。好きなように作り込んで下さい。 PokoXの上位版の予定でしたが、互換性は無くなります。当初はPokoX 2.0 を予定していましたが、後述の理由により名前を変更し、ゼロから作り直しました。 * 名前を変えた理由 "pokox"で検索すると、かなりの数のハンドルネームが引っかかってしまいました。このご時世、本当に「ユニーク」な名前を考案するのは至難の業のようです。新しい名称は、とりあえず 2007/04 - 06 現在、Google上では他に存在しないようです。 * 何故Pieceを使わないか フローエンジンのコアとなる、Piece-Flowと StageHand_FSM のソースを読んでも、何をやっているのかよく分からなかったというのが原因です。発端がそこで、「FSMワケわかんねー!」となり、「じゃあ、自分で作ってしまえ」と例により暴走が始まりました。ソースちゃんと読めよ、と言われても、暴走し出すと傲慢な性格がむき出しになりますので、何を言われても大して心に・・・とめないように、努力はしてます。 後付の理由ですが、ステートフルなページ遷移を行うフレームワークの仲間を増やしたいな、と。フレームワークじゃなくてライブラリですが。 * 今何やってるの? 2007-07-01 現在、目下コーディング & 単体テスト中で、機能をシェイプアップ中です。sf.netにプロジェクトは登録できましたが、まだCVSには入れていません。 この後の予定ですが、とりあえず一通り終わったら1.0.0-RCとしてCVSに入れて、リリースファイルに登録したら、そのままYakiBikiに移る予定です。8月位になるかな・・・。 YakiBikiでの実装によるフィードバックを入れて、とりあえず1.0.0としてリリースする予定です。ドキュメントもYakiBikiで書きたいところですし。 * Piece Framework 勉強会の影響 非常に刺激的でした。また、帰り道幾つか思いついた設計変更を。ここから下は作者以外の人は読み飛ばしちゃって下さい。 ** skipRender()の削除 よくよく考えたら、PageActorがreturn nullすればお仕舞いなので、二重にrender()の起動チェックをおこなう必要は無いと結論づけました。後述のXhwlay_Varsの導入とも関連しますが、なるべくXhwlay_AbstractRunnerインスタンスの持ち回りを避けたいというのもあり、もともとPageActorのエントリポイントに、skipRender()させたい為に Runnerインスタンスを渡していましたが、まあ、これで不要になるかなと。 ** Xhwlay_Vars の導入 今までは pageName や aci, bookmarkContainerId などを stdClass に押し込んでましたが、これも、Runnerインスタンスの持ち回りの遠因になっていましたのでやめます。 Xhwlay周りで値を持ち回したい場合は、Xhwlay_Vars を用いましょう、と。 Xhwlay_Vars::isset($key, $domain = '*') Xhwlay_Vars::unset($key, $domain = '*') Xhwlay_Vars::get($key, $default = null, $domain = '*') Xhwlay_Vars::set($key, $val, $domain = '*') define('XHWLAY_VARS_DOMAIN_DEFAULT', '*') あまり複雑な設定値を持たせたいわけではなく、単にスカラー値やサイズの小さい配列を入れる予定ですので、これで十分かと。refにするかは、ビミョー。 今後は、pageNameやaciなどは Xhwlay_Vars::get(XHWLAY_VARS_PAGE_KEY); // pageName Xhwlay_Vars::get(XHWLAY_VARS_ACI_KEY); // ACI Xhwlay_Vars::get(XHWLAY_VARS_BCID_KEY); // bookmarkContainerId みたいな感じにアクセスするようになります。def値も切っておかないと・・・。 また、XHWLAY_VARS_PAGE_KEYで取得できるのはあくまでも"filtered"された値になります。BCIDも同様。リクエストされた「生の」値を取得するには、 Xhwlay_Vars::get(XHWLAY_VARS_PAGE_KEY, '', XHWLAY_VARS_DOMAIN_REQUEST); Xhwlay_Vars::get(XHWLAY_VARS_BCID_KEY, '', XHWLAY_VARS_DOMAIN_REQUEST); とするようにすべきかもしれません。 書いておいてなんですが、Xhwlayでは基本的にクラスは単数形を使う方針ですので、Xhwlay_Var, XHWLAY_VAR_*となる可能性大。 ** Bookmarkコンテナの自動drop AbstractRunnerのコアレベルでは、Bookmarkのdropは行いますが Bookmark Container の drop はしていません。しかし、Bookmark Container ID が第二のセッションIDとなり得る以上、きちんとdropをしないとBCIDに対する固定値攻撃などが発生しかねません。 次の「総HOOK化」とも関連しますが、Bookmark の drop の後に、Bookmarkの数が0になっていれば Bookmark Container も破棄するようなHOOKをデフォルトで仕掛けておくべきでしょう。それに関連して、Bookmarkの数をcountするIFをBookmark Containerに追加しておくべきでした。 ** 総HOOK化 ・・・総、とまでは行かなくても、現段階でAbstractRunnerのimplement/extend先での実装を想定している setup(), terminate()などは、 push($hookpoint, $callback); pop($hookpoint); 的にすべきかもしれません。Xhwlay_Hookの登場か!?・・・それも、アリか・・・。 ** RunnerにgetInstance()を追加 総HOOK化とも関連します。また、開発者をどこまで野放しにするかのバランス感覚の話になります。当初は、PageActorの開発だけに注目して欲しくて、その外の世界にはあまり触らせないようにしていました。が、Hook化の事も考えると、どうしてもRunnerに触りたい、少なくともBookmark ContainerやConfig, Rendererに直に触りたいがその場合はまずRunnerにアクセスしなければならない。そうした時に、引数で一々Runnerの参照を渡すのも、かなり面倒です。 実はPageActorやBarrierActorのインターフェイスにもまだ迷いがあって、Xhwlay_Vars の導入を決めたのはこれら開発者用のIFがPokoXの時ほどお気楽・お手軽に決定できていない、というのがあります。であるならば、とりあえず情報にはアクセスできるようにしておき、徒に書き換えたときに何が発生するのかは保証外として単体テストもそこまでは通しませんよ・・・と線引きをしてもいいかな、というなんだか情けない妥協の結果が、Runner::getInstance()の導入経緯となります。 function execute(&$runner, $pageName, &$bookmark, $params) これが、今考えているPageActorのIFです。&$bookmark, $params は変える気はありません。どう考えても、今後設計が変わっても、必須のものであろうからです。しかし$pageName, $runnerは何の為にあるのか?と問われると、必須である理由はあまりなく単に「外部情報としてこの二つがあれば事足りるだろう」と考えていたからです。 しかし、外部情報として何が必要かは、たった一人の開発者が全てを予想できるものでは無いでしょう。であれば、もう、拘らずに全解放してしまっても良いのではないか。 もちろん Runner::setXXYY 系は軒並み、final & protected となり、規律としては外部から呼び出すことはできなくなります。get のみが public となります。 ・・・と書いてみたら、なんだか随分激しい仕様変更のような気もしてきました。特に「総Hook化」でのpop/pushは使い方次第ではコアのフローを完全に書き換えることができる悪魔のような仕様になりそうです。Hookの中でHookを書き換え、実行順序を変えたり、コアでデフォルトで設定されていたHookをPOPで破棄し、代わりに独自Hookを詰め直す・・・ということも可能。 うわぁ。 ** Story自体のBookmarkモードの取得や影響度についての修正。 PieceではStateFull/StateLessを、Flow自体ではなくUnityの方のYAMLで切り替えられるようになっていたのに触発されて。 一応Story自体にBookmarkモードを埋め込むことを「デフォルトの」使い方としますが、Runner側でも "requireBookmark" というHOOKポイントを用意しようかなと思います。 Runnerは現状、以下のコードでstory自体のBookmarkモードを検査しています。 if ( $this->_config->needsBookmark(...) ) { これが以下のようになります。本番はもうちょっとifブロックをまとめ直すかも・・・。 Xhwlay_Hook::invoke("requireBookmark"); $overWrited = Xhwlay_Var::get('requreBookmark', null); // 未設定であれば上書きは無かったとする。 if ( !is_null($overWrited) ) { if ( Xhwlay_Var::get('requiredBookmark') ) { // 上書きでBookmark ON } else { // 上書きでBookmark OFF } } else { if ( $this->_config->needsBookmark(...) ) { // 通常のStory/Page重視のBookmark ON ... 以上より、登録しておくべきHookは以下のようになります。処理はサンプルです。 class User_Hooks { function overwrite_story_bookmark() { $aci = Xhwlay_Var::get(XHWLAY_VAR_ACI_KEY); if ( $aci == 'anonymous' ) { Xhwlay_Var::set("requireBookmark", false); // ACI = 'anonymous'なら強制的にBookmarkOFF return; } $page = Xhwlay_Var::get(XHWLAY_VAR_PAGE_KEY); switch("$page.$aci") { case "page1.admin" : Xhwlay_Var::set("requireBookmark", true); // Bookmarkを強制的にON break; } } } Xhwlay_Hook::push(array('User_Hooks', 'overwrite_story_bookmark')); ・・・え・・・?何気なく書いちゃったけど、ACIとページ名によってHOOK側で強制的にBookmarkのON/OFFを切り替えられるのか?・・・ま、いっか。