gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

Slim docsの解析; Middleware

https://www.slimframework.com/docs/v3/concepts/middleware.html
ネタ的には「最重要クラス」に重要なネタなので、腰を据えて。


とりあえず、こちらの画像が一番わかりやすいんだろうなぁ、って思う。
https://www.slimframework.com/docs/v3/images/middleware.png


今まで書いてきたのはapp、つまりは「一番真ん中」で。
その外側に「Middleware 1」をラッピングして、さらにその外側に「Middleware 2」をラッピングして……的に、玉ねぎのような状態にする感じ。
なので、実行順番としては
・Middleware 2の入り口
・Middleware 1の入り口
・app(本体)
・Middleware 1の出口
・Middleware 2の出口
って感じで動く。エンジニアなら理解しやすい入れ子構造なんじゃないかなぁ?


個人的には(=MagicWeaponでは)、この辺を「クラスの継承」関係で書いてたんだけど。
いや「クラスの継承で書けばいいじゃん」って思わなくもないんだけど(問題なく書けてたし)、ただまぁ「後で付け足す」って感じで考えると、このMiddlewareって考え方は「面白いなぁ」って思うざますの。


なんて感じで「大まかに概念を把握した」ところで、さっそく、実装方法。
基本的には
・Middleware本体のクラスの記述
・Middlewareを「適用させる」ための、routerへの登録
の2種類の手順が必要ぽ。


Middlewareはどうも実際には「クラスでも関数でもOK」ぽいんだけど、まぁとりあえずクラス。
その場合「実装は__invoke()に書いてね」って感じらしい。
これ、多分「Middlewareを入れる場所を用意する」&「その辺をauto_loaderで読み込めるようにする」的なことが必要なんだろうなぁ。
ただその辺の考察は厳密には「今回の考察の範疇外」なので、今回は「index.phpにべた書き」とかいう、乱雑な方法を使いますw

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE:');
        $response = $next($request, $response);
        $response->write(':AFTER');
        return $response;
    }
}

$app = new \Slim\App;
$app->add(new ExampleMiddleware() );
$app->get('/', function(Request $request, Response $response, array $args) {
    echo "hoge test\n";
});
$app->get('/aaa', function(Request $request, Response $response, array $args) {
    echo "hoge aaa\n";
});

BEFORE::AFTERhoge test

あら。………あぁそうか「echoで取ってきてる」から、最後に足されちゃうのか。
ちょいと書き換え。

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE:');
        $response = $next($request, $response);
        $response->write(':AFTER');
        return $response;
    }
}

$app = new \Slim\App;
$app->add(new ExampleMiddleware() );
$app->get('/', function(Request $request, Response $response, array $args) {
    return $response->write("hoge test\n");
});
$app->get('/aaa', function(Request $request, Response $response, array $args) {
    return $response->write("hoge aaaa\n");
});

BEFORE:hoge test
:AFTER

はい予想どおり。


軽く、入れ子も確認。

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE:');
        $response = $next($request, $response);
        $response->write(':AFTER');
        return $response;
    }
}
class ExampleMiddleware2
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE 2:');
        $response = $next($request, $response);
        $response->write(':AFTER 2');
        return $response;
    }
}


$app = new \Slim\App;
$app->add(new ExampleMiddleware() );
$app->add(new ExampleMiddleware2() );
$app->get('/', function(Request $request, Response $response, array $args) {
    return $response->write("hoge test\n");
});
$app->get('/aaa', function(Request $request, Response $response, array $args) {
    return $response->write("hoge aaaa\n");
});

今度は/aaaにアクセス。

BEFORE 2:BEFORE:hoge aaaa

AFTER
AFTER 2

はい、きれいに入れ子


ふと疑問。「一部広域」「一部局所」だとどうなるんだろ?
多分「広域が一番外側」だと思われるんだけど。

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE:');
        $response = $next($request, $response);
        $response->write(':AFTER');
        return $response;
    }
}
class ExampleMiddleware2
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE 2:');
        $response = $next($request, $response);
        $response->write(':AFTER 2');
        return $response;
    }
}


$app = new \Slim\App;
$app->add(new ExampleMiddleware() );
$app->get('/', function(Request $request, Response $response, array $args) {
    return $response->write("hoge test\n");
})->add(new ExampleMiddleware2() );
$app->get('/aaa', function(Request $request, Response $response, array $args) {
    return $response->write("hoge aaaa\n");
});

まず/aaaは

BEFORE:hoge aaaa
:AFTER

なのでOK。
次、/にアクセス。

BEFORE:BEFORE 2:hoge test

AFTER 2
AFTER

うん予想通り。「全体→局所」で動くねぇ。


Group Middleware
あたりまでは大体
・想像できる動き
・「(無名含む)関数」は基本書くつもりなし
なので、放置。


Passing variables from middleware
ミドルウェアから属性を渡す最も簡単な方法は、要求の属性を使用することです。(機械翻訳)」。

$request = $request->withAttribute('foo', 'bar');

で値を設定して

$foo = $request->getAttribute('foo');

で使う、と。
ふむぅ……MagicWeaponのbagとか、突っ込んどこうかしらん?
とりあえず、ほんのり記憶はしておきませう。


Finding available middleware
むぅ。色々あるのかまぁそうか。
https://github.com/oscarotero/psr7-middlewares
https://github.com/slimphp/Slim/wiki/Middleware-for-Slim-Framework-v3.x
https://github.com/lalop/awesome-psr7


さて。
ちょりと気になったのが
・middlewareの中でcontainer使える?
・「Middlewareの途中でエラー」の出し方
の2つ。


「middlewareの中でcontainer使える?」
は、ググってみたら「コンストラクタで渡したら?」ってな記述があった。
うんまぁAppに相当する本体も「コンストラクタ渡し」なので、それでよいかなぁ。よし解決w


疑問の本命「エラー」。
予想してみたコード。

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        $response->write('Middleware Error!!');

        /*
        $response->write('BEFORE:');
        $response = $next($request, $response);
        $response->write(':AFTER');
        */
        return $response;
    }
}
class ExampleMiddleware2
{
    public function __invoke($request, $response, $next)
    {
        $response->write('BEFORE 2:');
        $response = $next($request, $response);
        $response->write(':AFTER 2');
        return $response;
    }
}


$app = new \Slim\App;
$app->add(new ExampleMiddleware() );
$app->get('/', function(Request $request, Response $response, array $args) {
    return $response->write("hoge test\n");
})->add(new ExampleMiddleware2() );
$app->get('/aaa', function(Request $request, Response $response, array $args) {
    return $response->write("hoge aaaa\n");
});

Middleware Error!!

よし1mmの狂いもなく予想的中w


比較的あっさりと終わったな。
さすがはSlim、全体的に思想がシンプルだ。素晴らしい。