私はSymfony 2が好きとは言えないが、Fabienの向かっている方向性が好きだ。
技術者社長としての精力的な活動。日本人ばりにワーカホリックかつ日本人よりレベルが高いと思う、
密結合による不便さの解消とDIの啓蒙(DI「コンテナ」にまで頭がまわってるかやや心配だが)、
他ライブラリの積極的な取り込み(Zend Frameworkに似通ってきたのは良い帰結だと見なす)、
法人をあげた開発体制づくり(進化を追求するあまりサポートが疎かなのは目をつぶる)、
エンタープライズフレームワークを求める姿勢(ディレクトリが深すぎるのはカスタマイズで何とかする)、
等々、欠点や偏りはあるが己の信念に向かって突き進む様は、ただただ感嘆するばかりだ。
PHPカンファレンス2009で「互換性は無いからな!」と言われたにもかかわらず、
結局カスタマイズバリバリのsymfony 1.4を愛用している身としては、
「あ、DebugPDOが無い」「あ、sfFinderが無い」など、細かいところでsymfony資産が使えない時につらくなる。
いっそのことSymfony 2を使ってsymfony 1.4を作れないものかとすら思う。
PEARのポータビリティとZend FrameworkやSymfony 2の現状ニーズ解決能力に、
Railsの「設定より規約」の思想と、(相反するが)カスタマイズ性の高さが加われば、最強じゃないかなーと思う。
パフォーマンスの最大化とデプロイ機能の強化も「最強」の要件だと思ってるけど、それは最悪自分でやる。
まあともかく将来的にはそうふう時代になって、PHP資産を扱いやすくなるであろう。わくわく。
閑話休題。いまの現実的にはsymfony 1の一部を他フレームワークに持ち込んで使うのは困難であるから、
「持ち込みやすい資産」に慣れておく/作っておくか、資産を持ち込まない方法を考えるしかない。
sfParameterHolderという、やや裏方の軽微なクラスがある。ここにもノウハウが凝縮されている。
このファイル自体に依存性は無いので、使いたいならこのファイルだけを設置すればいい。
もちろん、MITライセンスである事を明記した上で。
・・・という訳にもなかなかいかないから、いちいち手書きで実装する事が多い。
配列操作クラスを実装するには配列の直接の操作方法を理解しなければいけない。
以下にその方法を記す。
すべてPHPマニュアルに書いてあるノウハウではあるが、PHPをきちんと触った人にしかわかりにくい内容だと思う。
// 初期化(clear)
$parameters = array();
// 取得(get)
$value = $parameters[$name];
// nullを存在するとみなした上での存在確認(has)
array_key_exists($name, $parameters);
// nullやを存在しないとみなした上での存在確認
isset($parameters[$name]);
// falseやnullや""や0を存在しないとみなした上での存在確認
!empty($parameters[$name]);
// 既定値を伴う取得
// $value = (hasの内容 ? getの内容 : 既定値);
$value = array_key_exists($name, $parameters) ? $parameters[$name] : '既定値';
// 既定値の代入のみ
if (!array_key_exists($name, $parameters)) { $parameters[$name] = '既定値'; }
// 値のリスト取得
array_keys($parameters);
// 削除(remove)
unset($parameters[$name]);
// 代入(set)
$parameters[$name] = $value;
// 配列の上書き(add) 値がnullでもkeyがあれば上書きされる
array_merge($parameters, $add_parameters);
// 上書きはしないが配列に要素を追加する 値がnullでも上書きしない
$parameters += $add_parameters;
// serialize
serialize($parameters);
// unserialize
$parameters = unserialize($serialized);
PHPの構文に足りていないのは、hasとgetの部分であることがよくわかる。
「既定値を伴うget」は、addで代用できることもある。(sfActionのメンバ変数みたいなのに適用するのは微妙)
あるいはperlみたいに ||= が使えれば良かった。
// perl
$array{$name} ||= '既定値';
まあそれでも、既定値の問題は構文糖の問題に過ぎない。
しかし、hasの問題は言語仕様の根幹に関わる問題だと思う。
「何をもってして値がない状態とみなすか」という解釈が異なっており、3通りの答えが存在するからだ。
「PHPが未定義エラーを出さないこと」が定義だとすればarray_key_existsで良い。
「データベースからレコードを取ってきて、無い物は配列の既定値を適用する」なんて場合は、
issetやemptyを型などに応じて使い分けなければならない。つまり、統一的な解はない。
そういった使い分けの利便性まで考えると、あながち三項演算子作戦もデメリットばかりではないのかも知れない。
「3通りの答え」に対応した、リッチな配列操作クラスを作るのも一つの方法論だろう。
そこにはさまざまな配列機能を拡張するという余地も残されている。
しかし、PHPには配列操作クラスは無く、ただひたすら配列関数が連なっているという文化的背景を考慮すると、
あえてクラス化に頼らずに配列のままで答えを求めるのも、あるいは「漢のParameterHolder」と言えるかも知れない。
includeやクラス操作に関するオーバーヘッドを気にしなくて良くなるのも大きな利点だろう。
いままでのまとめ。
設計を考えた結果、意外にもMVCの原点に立ち返ってしまった。
今まで自分はMVC否定派だと思っていたがそんな事は無かったぜ。
MVCを正しく成立させるためには、M-V-Cがそれぞれ対等でなければいけないのだ。
symfonyもcakeもMが強すぎる。Vが弱すぎる。
その結果Cが調整弁として多くの役目を果たさねばならず、肥大化する羽目になる。
アクションをシンプルにするのはフレームワークの命題だと言えるので、それではいけない。
対等なMVC。
- コントローラ
- ルーティングを行う。ルーティングはアクションに相当する処理を行う。
- アクションに相当する処理にはすべて必ずルート定義が与えられる。アクション内で分岐が行われて2つの動作を1アクションに記述する事はありえない(禁止)。分岐が起きる場合、一方の処理は別アクションへの変更という形で処理する。
- ルート定義には従来通りワイルドカードを用いる事が出来る。標準の規約では、URLに該当するビューを(アクションが定義されていなくても)呼び出す事が出来る。ビューのファイル名はルート定義により決定される。
- よって、アクションはビューの面倒を見る必要性から解放される。Smartyのassignに相当する処理は暗黙に行われる。アクションによって明示的に変更しても良い。
- アクションに相当する処理は、原則的に設定ファイルに記述する事が出来る。個別のロジックが必要な場合は、そのロジックを指定して呼び出す。(これはstruts.xmlに似ている)
- ビュー
- ビューはassignされた変数群とファイルのパスを受け取る。
- ビューの設定はビュー専用の設定ファイルに記載される。
- ビューは「受け取ったパスから実際にどのファイルを呼び出すか」を設定する権限を有する。これはアクションのsetTemplate()に相当する。
- ビューはインクルード機能を有する。これを使い重複する実装をまとめる事が出来る。これはウィジェットの管理にも必須の概念である。
- ビューはDOCTYPE宣言やヘッダ送出のために、自身の親となるファイルをインクルードする事が出来る。これは設定ファイルに記述する。
- ビューはウィジェットを有する。たとえばPOSTリクエストを送信する方法はビューが決める。フォームなのかAjaxなのか、どんなインタフェースで送信させるのか、すべてビューが決める。
- 以上の要件から、Webデザイナーは以下の事項を学習する事で開発に参加する事が出来る。
- ビューの設定ファイルの記述方法
- テンプレートエンジンに対応するコーディング方法
- ウィジェットの定義と利用方法
- そして、当該プロジェクトにおける個別のルート定義(先行開発の場合はワイルドカード)
- モデル
- 機能群とテーブルは一対一で結びつかないという経験から、機能群とは異なる。あくまでテーブルに関する操作しか実装されない。
- ロジック
- ロジックはMVCから分離されるが、MVCの要素をそれぞれ内包する。(これはcakeの考え方に近いのかも知れないが、真意はわからない)
- モデルを操作するための機能群が含まれる。
- コントローラに追加のコーディングが必要な場合はロジックが呼び出される。
- ビューのウィジェットを実装するのに必要であればロジックが呼び出される。
- ライブラリ
- ロジックはプロジェクトに対して実装されるもので、ライブラリはフレームワークに対して実装されるものであると定義する。
- ライブラリはロジックの代替になることが出来る。もちろんロジックからライブラリを呼び出すことも出来る。
これでだいたい、作りたいフレームワークの骨格は設計できた。
あとは具体的に実装をはじめていこうと思う。
オープンソースプロジェクトにしたいのだが、作法がわからない。