開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. [PHP] 流行りのPSR-7 ミドルウェアが複雑すぎて人生が辛い
[PHP] 流行りのPSR-7 ミドルウェアが複雑すぎて人生が辛い

[PHP] 流行りのPSR-7 ミドルウェアが複雑すぎて人生が辛い

前回の[PHP7] PSR-7 Middleware用の代替シグネチャ(HttpHandler / HttpContext Interface)の提案について引き続き考えています。

今回は、内容をわかりやすく図解してみようと思います。

 

popular-middleware.pngcontext-middleware.png
 

流行りのPSR-7ミドルウェア

心の中では「玉ねぎ」という呼称が定着しかけています。

大雑把な処理のフローは

  • リクエストとレスポンスを修正(前処理)
  • 処理を次のミドルウェアに任せる(スキップも可)
  • 返ってきたレスポンスを修正(後処理)する

となります。図解を再掲します。

popular-middleware.png

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というクラスを作ります。

context-middleware.png

ミドルウェアは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で妥協すれば使えると思います。

  • posted by mori
  • PSR
TOPに戻る