1. asaokamei

    Posted

    asaokamei
Changes in title
+PSR-7のミドルウェアは、何故ああなのか?
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,96 @@
+PSR-7のフレームワークとしては、[zend-expressive](https://github.com/zendframework/zend-expressive)、[SlimPHP/Slim 3](http://www.slimframework.com/)、そしてフレームワークではありませんが[Relay/Relay](http://relayphp.com/)などが出てきました。
+
+嬉しいのは、ミドルウェアのAPIが全て同じことです。
+
+```php
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+function __invoke(
+ ServerRequestInterface $request,
+ ResponseInterface $response,
+ array $args);
+```
+
+ひとつミドルウェアを作れば、上記3つのフレームワークのどれでも使いまわせることになります。
+
+なんて素晴らしい!
+
+でも、**なぜ、ミドルウェアはこの形なのでしょう?**
+
+### Express.JS
+
+この形は、node.js用のウェブアプリケーションフレームワークである[Express.js](http://expressjs.com/ja/)のミドルウェアと同じ、という説明があります。でも、答えになってない!
+
+疑問が頭から離れず夜も寝られなくて。やっといくつか理由を考えついたので書いてみます。
+
+
+
+ServerRequestInterface `$request`
+---------------------------------
+
+まずは最初の引数、`$request`から。
+
+これがないと、なんの処理をしていいのか皆目わからない。ので、必須なのは自明、と言った感じでしょうか。
+
+ちなみに。
+
+Symfonyや、同じコンポーネントを使っているStackPHPでのミドルウェアは、`$request`だけにフラグが2つという構造になっています。([HttpKernelInterface](https://github.com/symfony/http-kernel/blob/3.0/HttpKernelInterface.php)参照)
+
+> 例えばAPIを`$request`だけにしてしまえば、簡単で分かりやすいのに、なぜ3つも引数が増えてしまうのだろう?というのが疑問の始まり。
+
+
+ResponseInterface `$response`
+-----------------------------
+
+次は`$response`。
+
+ミドルウェアに、なぜレスポンスオブジェクトが必要なのか?
+
+PSR-7や最初に説明したミドルウェアの大事な点として、**フレームワークに依存しないコードを書きたい**というのがあります。
+
+例えば認証用ミドルウェアなどは、認証に失敗した場合にレスポンスを返す場合があります。**フレームワークに依存しないということは、オブジェクトを生成できない**、と同じことだと気が付きました。
+
+実際にPSR-7には**オブジェクト生成に関するAPIはありません**。
+
+なので、レスポンスオブジェクトを渡してもらう必要があるわけです。
+
+
+callable `$next`
+----------------
+
+最後の`$next`というコーラブル変数。これは、こんな感じで使います。
+
+```php
+function __invoke($req, $res, $next) {
+ // ここで前処理をする!
+ if($next) {
+ $res = $next($req, $res); // next, please!
+ }
+ // ここで後処理もできる!
+ return $res;
+}
+```
+
+この形の良い所は、**次のミドルウェアを呼ぶ前と後の両方で処理を書ける**ことです。
+
+ところで`$next`がない場合は、どうやって後処理をするのでしょう?
+
+Symfonyの場合は、[TerminableInterface](https://github.com/symfony/http-kernel/blob/3.0/TerminableInterface.php)というのを使います。ミドルウェアが`TerminableInterface`を実装している場合は、*アプリの実行が終了*したら、`terminate`メソッドが呼ばれることになってます。
+
+> 多分。実は確認できないので、間違ってるかも。
+
+突然、**面倒な感じになってきました**。
+
+**メソッド一つで**、かつ**前後の処理ができる**、と考えると、この形が一番簡単で明快な方法な気がしてきました。
+
+最後に
+====
+
+今のAPIで最初に疑問に思ったのは、`$next`が入ることでした。本来の処理に必要な`$request`と`$response`以外に、次のミドルウェアを呼び出すという、別の責務がミドルウェアに発生するからです。
+
+> このAPIでミドルウェア作ると、他の形に応用しずらい気がするんですよね。理由は`$next`が入り込んでくるから。
+
+でも、この形が一番シンプルになる気がしてきました。やはり使われるコードというのは理由があるのですね。後から考えると当たり前な結論な気がしますが、考えている間は面白かったです。
+
+> なお、この形が**本当に標準のようになるかは、まだ分かりません。**