読者です 読者をやめる 読者になる 読者になる

StackPHPを使ってみる(Middlewareを作る)

PHP

自分でMiddlewareを作る

ひな形はこんな感じ。

<?php
// 何もしないミドルウェア
class MyMiddleware implements HttpKernelInterface {
    private $app;
    public function __construct(HttpKernelInterface $app, array $options = []) {
        $this->app = $app;
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        return $this->app->handle($request, $type, $catch);
    }  
}

テンプレートエンジンをMiddlewareとして実装してみる

Mustacheを使ってみる。一番単純に実装するとこんな感じ。

<?php
class Mustache implements HttpKernelInterface {
    private $app;
    private $engine;
    public function __construct(HttpKernelInterface $app, array $options = []) {
        $this->app = $app;
        $this->engine = new Mustache_Engine($options);
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $request->attributes->set('mustache', $this->engine);
        return $this->app->handle($request, $type, $catch);
    }  
}

使う側はこんな感じ。

<?php
$app = new CallableHttpKernel(function (Request $request) {
    $token = $request->attributes->get('oauth.token');
    if (!$token) {
        return new RedirectResponse('/auth');
    }

    $params = $token->getExtraParams();
    return new Response($request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]));
});

// ...

$stack = (new Stack\Builder())
    ->push('Stack\\Session')
// ...
    ->push('Mustache');

returnがかっこ悪いのでヘルパーメソッドにするとかTraitを使うのが良いかもしれない。その前にそろそろCallableHttpKernelから卒業しよう。

<?php
class MyApplication implements HttpKernelInterface {
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $token = $request->attributes->get('oauth.token');
        if (!$token) {
            return new RedirectResponse('/auth');
        }

        $params = $token->getExtraParams();
        return new Response($request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]));
    }
}

さてrenderメソッドを実装してみようと思ったんだけど、これこうなっちゃうな。

    public function render($template, $params, $request)
    {
        return new Response($request->attributes->get('mustache')->render($template, $params));
    }
// 又は
    public function render($template, $params, $engine)
    {
        return new Response($engine->render($template, $params));
    }

MiddlewareはRequestを受け取ってResponseを返すだけなので、Middlewareからアプリケーションに何かを渡すには$request->attributes経由ということになるんだけど、そうするとrenderメソッドの引数が1つ増えることになってイマイチ。 この辺がStackPHPを使う上でのキモかもしれない。

あるいはこう書けるようにした方がいいか。

        return $request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]);

Mustache_Engine#render()は文字列を返すけどHttpKernelInterface#handle()はResponseを返さなければならないので、単にMustache_Engineのインスタンスを返すのではなく、render()メソッドの生えた何かを返すようにしよう。本来は別のクラスを作った方が良さそうだけど単純にMiddlewareにrenderメソッドを実装してしまおう。

<?php
class Mustache implements HttpKernelInterface {
// ...
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $request->attributes->set('mustache', $this);
        $request->attributes->set('mustache.engine', $this->engine);
        return $this->app->handle($request, $type, $catch);
    }

    public function render($template, $params)
    {
        return new Response($this->engine->render($template, $params));
    }
}

あと、renderの引数はファイル名にしよう。

$stack = (new Stack\Builder())
    ->push('Stack\\Session')
// ...
    ->push('Mustache', [
        'loader' => new Mustache_Loader_FilesystemLoader(__DIR__ . '/views'),
    ]);

ここでMustache_Loader_FilesystemLoaderをnewするのは好きじゃない人もいるかもしれない。stack-sessionなんかは内部にPimpleのインスタンスを持っていて実行時に評価されるようにしているようだ。

テンプレートを作る。Mustache_Loader_FilesystemLoaderのデフォルトでは拡張子".mustache"を要求する。

<!-- views/index.html.mustache -->
<h1>Hello, {{ name }}</h1>

使う方はこうしておこう。

        $mustache = $request->attributes->get('mustache');
        return $mustache->render('index.html', ['name' => $params['screen_name']]);

https://github.com/iakio/stackphp-example