[PHP] 流行りのPSR-7 ミドルウェアが複雑すぎて人生が辛い
前回の[PHP7] PSR-7 Middleware用の代替シグネチャ(HttpHandler / HttpContext Interface)の提案について引き続き考えています。
今回は、内容をわかりやすく図解してみようと思います。
流行りのPSR-7ミドルウェア
心の中では「玉ねぎ」という呼称が定着しかけています。
大雑把な処理のフローは
- リクエストとレスポンスを修正(前処理)
- 処理を次のミドルウェアに任せる(スキップも可)
- 返ってきたレスポンスを修正(後処理)する
となります。図解を再掲します。
Chain of Responsibilityのような形ですね。ミドルウェアの処理を組み合わせることが念頭にあります。次のミドルウェアに任せる、と言う部分がミドルウェアのメソッドのシグネチャにも表れており
use Psr\Http\Message\ServerResponseInterface;
use Psr\Http\Message\RequestInterface;
function (
ServerRequestInterface $request, //HTTPリクエスト
ResponseInterface $response, //HTTPレスポンス
callable $next //次のミドルウェア
) {
// ...
}
となっています。
このcallable $nextに気に食わない点があったのが違うタイプのミドルウェアを探した理由です。引数でcallableの型(callableが何を引数として何を返すか)を明示できないということもあります。1つのミドルウェアがアプリケーション全体を意識していくone for allの精神を感じます。その心意気は嫌いじゃないですが、余計な責務が発生してやいませんか。
アイデア
この玉ねぎ型のミドルウェア、JavaScript界隈のExpressからの輸入だと言われています。
玉ねぎ型ミドルウェアを考えた人も、本当はもっとシンプルなミドルウェアの合成を実現したかったに違いないのです(偏見。
根本にあるのは、JSerがコールバック地獄に慣れすぎていること関数が返り値を一つしか返せないこと、ではないかなあと想像しています。
もし関数がRequestもResponseもまとめて返せれば、次々に同じ型の関数で処理ができます。なら、RequestとResponseまとめた型作れば良くね?、と
HttpContextを利用するミドルウェア
RequestとResponse(とその他の状態)を保持するHttpContextというクラスを作ります。
ミドルウェアはHttpContextを受け取って、新たなHttpContextを返すだけです。わー簡単ですね!
ミドルウェアを直列でつなぐパイプラインが簡単に実現できますし、玉ねぎ型もエミュレートできます。
シグネチャというかインターフェースは、例えば下記のようになります。
// Middleware
interface HttpHandlerInterface {
public function __invoke(HttpContextInterface $context) : HttpContextInterface;
}
// Http Context (Request + Response + State)
interface HttpContextInterface {
public function getRequest() : ServerRequestInterface;
public function getResponse() : ResponseInterface;
public function isTerminated(): bool;
public function withRequest(ServerRequestInterface $request): HttpContextInterface;
public function withResponse(ResponseInterface $response): HttpContextInterface;
public function withIsTerminated(bool $isTerminated): HttpContextInterface;
public function handledBy(HttpHandlerInterface): HttpContextInterface;
}
※ミドルウェアをHttpHandlerと呼称。
利用イメージは前回の記事を参照してください。
GitHubにリポジトリ作りました(α)
HttpContext: n1215/http-context - GitHub
ミドルウェアのサンプル:n1215/http-middleware-sample - GitHub
PHP7です。PHP5系でも返り値のタイプヒント外してPHPDocで妥協すれば使えると思います。