SilexのルーティングをFlask風に

You can read this in English here

PythonのマイクロフレームワークであるFlaskを知っていますか?
Python版sinatoraといったところでしょうか。
このFlaskでは 'Variable Rules' という機能が用意されています。

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    pass

"int:", "float:", "path:"という接頭辞をルーティングのパラメータに追加することができます。
たとえば"int:"を追加した場合は、そのパラメータは整数型のみを受け付けるという意味になり、"path:"を追加した場合は"/"を含む文字列も受け付けるという意味になります。

同じことをSilexで実現しようとすると、次のようになります。

<?php
...
$app->get('/post/{post_id}', function ($post_id) {
    ...
})
->assert('post_id', '\d+')

assertというメソッドを使ってパラメータの条件を追加することができます。詳細は ドキュメントのRequirementsあたりを見てみてください.

ただ、Flaskのほうがすっきりしていて良いなぁと思ったので、Flask風に指定できる機能をSilexに追加してみました。さすがにコア部分にも手を加える必要があったので、コード一式はGitHubのブランチに置いてあります。

このコードを利用すれば、以下のような書き方ができます

<?php
...
$app = new Silex\Application();
$app['route.compiler_class'] = 'Silex\\RouteAssertCompiler';

$app->get('/foo/{int:id}/{path:name}', function ($id, $name) {
    return sprintf("%d:%s", $id, $name);
});

Flask風ですっきりしましたw

この機能を実現するために新しい "RouteAssertCompiler"というクラスを用意し、この自前コンパイラークラスをApplicationで指定できるように魔改造しました。なので、'route.compiler_class'というパラメータに'Silex\\RouteAssertCompiler'をセットするだけでFlask風ルーティングになります。

"/foo/3/a/b/c" にアクセスすると、"3:a/b/c" が返されますし、"/foo/a/b/c"でアクセスされた場合はルーティングに一致しません。

これはこれでシンプルで好きですし、Silexのassert, value, convertメソッドを使えば細かく制御できるという点で柔軟性がありますよね。

ちなみに、この魔改造バージョンでもassert,value,convertメソッドも使えます。