この連載のパート2の結論において、Symfony2 コンポーネントを使うことによる大きな恩恵を話しました: コンポーネントを使うすべてのフレームワークとアプリケーションのあいだの 相互運用性 です。 HttpKernelInterface
を実装するフレームワークをつくることでこのゴールに向かって大きく前進しましょう。:
namespace Symfony\Component\HttpKernel; interface HttpKernelInterface { /** * @return Response A Response instance */ function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); }
HttpKernelInterface
は冗談抜きに HttpKernel コンポーネントの中でもっとも重要なピースでしょう。このインターフェイスを実装するフレームワークとアプリケーションはじゅうぶんな相互運用性があります。さらに、たくさんのすばらしいフィーチャがそのままついてきます。
このインターフェイスを実装するようにフレームワークをアップデートします。:
<?php // example.com/src/Framework.php // ... use Symfony\Component\HttpKernel\HttpKernelInterface; class Framework implements HttpKernelInterface { // ... public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) { // ... } }
この変更がささいなものであれ、たくさんのものがもたらされます!もっとも印象に残るものの1つ: 透過的な HTTP キャッシング サポートについて語りましょう。
HttpCache
クラスは PHP で書かれたフルフィーチャのリバースプロキシを実装します; HttpKernelInterface
を実装し
HttpKernelInterface
インスタンスを包み込みます。:
// example.com/web/front.php $framework = new Simplex\Framework($dispatcher, $matcher, $resolver); $framework = new HttpKernel\HttpCache\HttpCache($framework, new HttpKernel\HttpCache\Store(__DIR__.'/../cache')); $framework->handle($request)->send();
HTTP キャッシングサポートをフレームワークに追加するために必要なことはこれですべてです。驚きました?
キャッシュ機能の調整は HTTP キャッシュヘッダー経由で行うことが必要です。たとえば、10秒のあいだにレスポンスをキャッシュしたいのであれば、 Response::setTtl()
メソッドを使います。:
// example.com/src/Calendar/Controller/LeapYearController.php public function indexAction(Request $request, $year) { $leapyear = new LeapYear(); if ($leapyear->isLeapYear($year)) { $response = new Response('Yep, this is a leap year!'); } else { $response = new Response('Nope, this is not a leap year.'); } $response->setTtl(10); return $response; }
Tip
筆者のように、リクエストをシミュレートすることでコマンドラインからフレームワークを実行している場合 (Request::create('/is_leap_year/2012')
) 、Response のインスタンスを文字列表現 (echo $response;
) として吐き出させることでかんたんにデバッグすることできます。文字列表現にはレスポンスのコンテントと同じようにすべてのヘッダーが含まれます。
これが正しく動くのか検証するためには、レスポンスのコンテントにランダムな数値を追加し、10秒ごとに数値だけが変わることをチェックします。:
$response = new Response('Yep, this is a leap year! '.rand());
Note
プロダクション環境にデプロイするとき、Symfony2 のリバースプロキシを使い続けるか (共用ホスティングの用途にすぐれています) もしくはよりよい方法は、 Varnish のようなより効率的なリバースプロキシに切り替えることです。
アプリケーションキャッシュをマネージするために HTTP キャッシュヘッダーを使うやりかたはとても強力で、HTTP の仕様の有効期限とバリデーションモデルの両方を使うことができるので、キャッシング戦略をきめ細かく調整できます。これらのコンセプトを快適に感じないのであれば、 HTTP キャッシング の章を読んでいただくことをおすすめします。
Response クラスにはたくさんのメソッドが用意されているので、HTTP キャッシュの調整をとてもかんたんにできます。もっとも強力なものの1つは setCache()
でもっともよく使われるキャッシング戦略を1つのシンプルな配列に抽象化します。:
$date = date_create_from_format('Y-m-d H:i:s', '2005-10-15 10:00:00'); $response->setCache(array( 'public' => true, 'etag' => 'abcde', 'last_modified' => $date, 'max_age' => 10, 's_maxage' => 10, )); // 上記のコードは次のコードと同等です $response->setPublic(); $response->setEtag('abcde'); $response->setLastModified($date); $response->setMaxAge(10); $response->setSharedMaxAge(10);
バリデーションモデルを使うとき、 isNotModified()
メソッドによってレスポンスの生成を可能なかぎり短縮することでレスポンスの時間をかんたんに短くすることができます。:
$response->setETag('whatever_you_compute_as_an_etag'); if ($response->isNotModified($request)) { return $response; } $response->setContent('The computed content of the response'); return $response;
HTTP キャッシングを使うことはすばらしいですが、ページ全体をキャッシュできないとしたらどうでしょうか? 動的なサイドバー以外の残りのコンテンツをキャッシュしたい場合は?Edge Side Includes (ESI) が助けになります!コンテンツ全体を生成する代わりに、ESI によってページの領域をサブリクエスト呼び出しのコンテンツとしてマークできます。:
This is the content of your page Is 2012 a leap year? <esi:include src="/leapyear/2012" /> Some other content
HttpCache によってサポートされる ESI タグに関して、 ESI
クラスのインスタンスにそれを渡す必要があります。 ESI
クラスは自動的に ESI タグをパースし、これらを適切なコンテントに変換するためにサブリクエストを作成します。:
$framework = new HttpKernel\HttpCache\HttpCache( $framework, new HttpKernel\HttpCache\Store(__DIR__.'/../cache'), new HttpKernel\HttpCache\ESI() );
Note
ESI を動かすために、Symfony2 の実装のようなリバースプロキシを使う必要があります。 Varnish は最良の代替ソフトウェアです。
複雑な HTTP キャッシングストラテジと/またはたくさんの ESI のインクルードタグを使うとき、リソースをキャッシュすべきかどうかを理解するのは困難な状況があり得ます。デバッグを楽にするために、デバッグモードを有効にすることができます。:
$framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI(), array('debug' => true));
デバッグモードではキャッシュレイヤーが行ったことをそれぞれのレスポンスに X-Symfony-Cache
ヘッダーを追加されます。
X-Symfony-Cache: GET /is_leap_year/2012: stale, invalid, store
X-Symfony-Cache: GET /is_leap_year/2012: fresh
HttpCache には多くのフィーチャが備わっており、
RFC5861 で定義された stale-while-revalidate
と stale-if-error
HTTP Cache-Control
エクステンションが用意されています。
単一のインターフェイスに加えて、HttpKernel コンポーネントに組み込まれている多くのフィーチャから恩恵を得ることができます; HTTP キャッシングはそれらの1つでしかありませんが、我々のアプリケーションを羽ばたかせるために大切なフィーチャです!