PHPの次の10分のためのクールなマイクロフレームワークを勢いでリリースしました
いったい、一日に何個のフレームワークが生まれているのだろう。そんな疑問が浮かぶほど、PHPによる (Webアプリ) フレームワークの数は尋常じゃない・・・という時は過ぎ、5.3系フレームワークの開発が落ち着きを見え始めたのが昨今だ。
ま、というわけでGeneratorでルーティング書いたらえげつなくないってのかを確かめようと思って、勢いでやってみた。
コンセプト
HTTP リクエストに対してレスポンス返すんだろ。リクエストパスはリクエストの一部なだけでしょ。 コードで言うと、こう
<?php function() { // 左のリクエストがマッチしたらtrueを返すようにし、右のアクションを実行してレスポンスを返す yield function ($request){} => function (){return $response;}; }
特徴
- PHP 5.5で動く
- コントローラ = コールバック
- サービスロケータ - アクション内で$this->get('key')で取得
- ルーティングでのテンプレートファイルへの自動マッチ
- アプリケーション側でのディレクトリ制約はない。(ハードコードはなく、設定で変えられる)
Hello world
<?php $routing = call_user_func(function () { yield '/hello/:foo' => function ($foo) { return "Hello $foo"; }; }); (new Dispatcher($routing))->dispatch(new Request)->send();
ヴァリデーション
これはREADMEに書いた。ValidationErrorなどのコンティニューフラグを返却すると、ジェネレータのループが続行します。
<?php use Backbeard\Dispatcher; use Backbeard\ValidationError; use Zend\Http\PhpEnvironment\Request; $routing = call_user_func(function () { yield '/hello/:foo' => function ($foo) { return "Hello $foo"; }; $error = (yield ['method' => 'POST', 'route' => '/entry/:id'] => function ($id) { if ($this->get('request')->getPost('NAME') == 'wtf') { return ['var1' => 'baz']; // will be render entry.mustache } else { return new ValidationError(['error']); } }); yield '/entry/:id' => function ($id) use ($error) { $message = $error ? current($error->getMessages()) :''; return "Hello $id ".$message. '<form method="POST" action="/entry/'.$id.'">'. 'NAME<input type="text" name="NAME">'. '</form>'; }; yield function(Request $request) { return $request->getRequestUri() === '/'; } => function () { $response = $this->get('response'); $response->setContent("Hello"); return $response; }; }); (new Dispatcher($routing))->dispatch(new Request)->send();
上の例では触れてないけど、キー側(ルーティング)側でシステム上の期待値(認証があるかなど)を判定し、アクション側で業務ロジックでのヴァリデーションをかけるようにすればスッキリなりそうな気がしてる。
アプリケーションスケルトンのインストール
composerで以下のコマンド実行します
php composer.phar create-project -s dev sasezaki/backbeard-skeleton path/to/install
途中思ったこと
- dispatch(new Request, new Response)を全面にだしたけど、DICなりとのあとあと整合性考えるとやっぱ起動するためのApplicationクラスに閉じ込めたほうがいいなと
- テンプレートへの自動マッチにZend\Mvc\Router\RouteMatchのgetMatchedRouteName()とか使ったんだけど、 zf3のプロポーザルだとなくなりそうなのよね https://github.com/DASPRiD/Dash/blob/master/src/Dash/Router/RouteMatchInterface.php
- Silexのboot()メソッドは多くで空実装されてるのを見ました。てかこれサービスではなくイベントでは.. https://github.com/silexphp/Silex/blob/master/src/Silex/ServiceProviderInterface.php
Pull Requestについて
お待ちしておりません。
謝辞
以下のエントリの内容をパク、、参考にさせていただきました。本当にありがとうございました。
- http://d.hatena.ne.jp/moriyoshi/20080930/1222735480
- http://d.hatena.ne.jp/anatoo/20100116/1263620509
- http://d.hatena.ne.jp/brtRiver/20120609/1339269807
- http://dqn.sakusakutto.jp/2013/06/ethnam27.html
コード
https://github.com/sasezaki/Backbeard
最後に
ここらへんまでコード含めて隅々読むと大体10分です。