Slimとかあるけど、僕は別に好きじゃないんだ。
はじめに
要はWebアプリってHTTPリクエストから任意の函数を起動できりゃいいのだけれども、函数の実体は単なるデータに過ぎないわけで、ルーターの仕事はメソッドとPathから固有の値を返してくりゃ、それだけでいい。
そんなこんなでTeto Routingを作った。以下のようなコマンドでインストールできる。
composer require zonuexe/simple-routing
ルーターをプロジェクトに組み込む方法はインスパイヤされて掲示板を作りたくなった(3)に書いた。
ルーティング定義
// Method Path ReturnValue Param => RegExp extension (format)
$routing_map = [
['GET', '/', 'index' ],
['GET|POST', '/search', 'search' ],
['GET', '/article/:id', 'article', ['id' => '/^(\d+)$/'], '?ext' => ['', 'txt']],
['GET', '/info', 'feed' , '?ext' => ['rss', 'rdf', 'xml']],
'#404' => 'not_found' // special
];
$router = new \Teto\Routing\Router($routing_map);
$action = $router->match($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
$routing_map
は、ただの入れ子になった配列だ。'#404' => 'not_found'
だけがちょっと特別で、これはpathがどのパターンにもマッチしなかったときに返す値を指定する。
拡張子マッチ'?ext' => ['', 'txt']
は奇妙に見えるかもしれないが、/article/1234
と/article/1234.txt
のどちらにもマッチする。決して/article/1234.
にはマッチしない。
'?ext' => ['*']
でワイルドカード指定することもできる。/article/1234.txt
にも/article/1234.xls
にもマッチする。ただし/article/1234
にはマッチしないので、そのときは'?ext' => ['', '*']
と書けば良い。
多くの軽量フレームワークやルーターは $router->get('/article/:id', function($id){ ... });
のようなメソッド呼び出しによるルーティング定義を採用するが、zonuexe/simple-routingでは意図的にそのような形式を採らなかった。
この方式での定義がつらくなるような規模のWebアプリケーションは、相応のフレームワークを採用するべきではなかろうか。LaravelとかCakePHPとか。よく知らないけど。
Actionオブジェクト
$router->match()
の返り値はActionクラスのインスタンスオブジェクトを返す。
$action->value
でルーティングに定義した返り値が得られるし、$action->param
でpath中のパラメータの値を連想配列として取得できる。
実装例
要は最終的に配列になってればいいので、こうやって書くことも可能。
<?php
$routing = [];
$routing['#404'] = function(){
// 404の処理
};
$routing[] = ['GET', '/hoge', function(){
// hogeの処理
}];
$routing[] = ['GET', '/fuga/:id', function($id){
// fugaの処理
}, ['id' => '/^[1-9]\d*$/']];
$router = new \Teto\Routing\Router($routing);
$action = $router->match($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
call_user_func_array($action->value, array_values($action->param));
ルーターは任意の値を返すことができるので、クロージャーを返すようにすれば、Slimに似た感じの書きかたにもできる。
もうちょっとした例
あとがき
こんなことをやってるから、僕の仕事は遅いことに定評がある。