2015年6月12日金曜日

PSR7用のヘルパーとレスポンダー

コツコツとフレームワークを作っていて。
ふと、自分が欲しかったのは、もっと簡単なことじゃないか?と思い直して作ったのがTuum/Http」というパッケージ。

【修正:2015/06/26】
Tuum/Responder」に名前を変更しました。

そもそものスタートとして、SlimやStackPHPの簡潔な構造に憧れたところから始まっている。が、実際に使うとなると、API作るには便利だけれど、普通のウェブサイトを構築するには作業が面倒そうだなぁと。

面倒だと思った部分は、レスポンスを返す部分。たとえば前のページに戻ったり、ついでにメッセージやエラー情報を付加したりという部分。同じような処理が多い割には、細かな設定が必要な気がする。

そこで、この部分だけをパッケージにしてみた。


何をするパッケージなのか



大きくヘルパーとレスポンダーからできている。
大事なのはレスポンダーの方。

ヘルパー

Psr7に足りなさそうな機能をスタティックなメソッドとして提供している。見れば一発、簡単なものばかり。例えば、

$bool = ResponseHelper::isRedirect($response);

はリダイレクトレスポンスかどうかをチェックできる。RequestHelperとResponseHelperの2つがある。

レスポンダー

レスポンスを構築するためのヘルパー。
例えば、別パスにリダイレクトしたり、その際にメッセージを付加できる(単にセッション・フラッシュに登録してるだけだが)。

Redirect::forge($request, $response)->withMessage('welcome!')->toPath('jump/to');

// ...now in the subsequent request to a server...
Respond::forge($request, $response)->asView('template'); // with the 'welcome!' message.

次のリクエストの際に、前のメッセージが自動でビューに入ってくる。メソッド名がどこかで見たことのあるのは愛嬌。使いやすいと思ったので。

レスポンダーとしては、RespondRedirect、そしてErrorがある。それぞれ、ビューを返す、リダイレクトを返す、エラーページを返す。

使うのに必要なこと:サービス


レスポンダーの機能を実現するために、ビューやセッションなどのサービスを使っている。これを作っておいて、レスポンダーに渡す必要がある。

セッション用インターフェース

セッションのフラッシュを使ってリクエスト間のデータを受け渡している。そのためのセッションとしてSessionStorageInterfaceという形で定義した。

と行っても、実際はAura.SessionのSegmentと同一のAPI。これを使うのが前提みたいになっている。

use Aura\Session\SessionFactory;

$factory = new SessionFactory();
$session = $factory->newInstance($_COOKIES);
$segment = $session->getSegment('some-name');

ここで作った$segmentがSessionStorageInterfaceと同じオブジェクトになる。

これを$requestに設定する。
use Tuum\Http\RequestHelper;
$request = RequestHelper::withSessionMgr($request, $segment);

あるいは、後で書くようにコンテナに設定しておく方法もある。

ビュー用インターフェース

HTMLを返すにはテンプレートを使ったビューを使いたい。そのために、ビューを展開するViewStreamInterfaceを定義してみた。

テンプレートを展開できるStreamとして扱う。

ViewDataクラス

これは実際のクラス。
ビューやリクエスト間でデータをやりとりするためのデータ転送用オブジェクト。

ビュー用のクラスはViewDataを理解して、実際のレンダラーに渡す必要がある。


コンテナー用インターフェース

サービスを管理するコンテナ。Container-Interopで定義したインターフェースを使った。これに必要なサービスを登録しておいて、$requestに設定する。

次はセッションを設定する方法。

$app = new Container(); // must implement ContainerInterface
$app->set->(SessionStorageInterface::class, $segment);RequestHelper::withApp($request, $app);