今日のトピックに進む前に、フレームワークを少し変更して、テンプレートをさらに便利にします。
<?php // example.com/web/front.php require_once __DIR__.'/../src/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $request = Request::createFromGlobals(); $map = array( '/hello' => 'hello', '/bye' => 'bye', ); $path = $request->getPathInfo(); if (isset($map[$path])) { ob_start(); extract($request->query->all(), EXTR_SKIP); include sprintf(__DIR__.'/../src/pages/%s.php', $map[$path]); $response = new Response(ob_get_clean()); } else { $response = new Response('Not Found', 404); } $response->send();
クエリ配列から現在のシンボルテーブルに変数をインポートしたため、次のようにhello.phpテンプレートを簡素化します。
<!-- example.com/src/pages/hello.php --> Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>
これで、新しい機能を追加する方がはるかに便利になります。
サイトの重要な側面は、リンクの形式です。 リンクマップのおかげで、連想応答を生成したコードからそれらを分離しましたが、まだ十分な柔軟性がありません。 たとえば、パラメータ化された文字列ではなく、動的に生成されたパスからデータを処理する場合はどうなりますか?
# Before /hello?name=Fabien # After /hello/Fabien
この機能をサポートするには、Symfony2コンポーネント-ルーティング(Symfony2 "Routing"コンポーネント)を使用します。 いつものように、コンポーネントをcomposer.jsonに追加し、
php composer.phar update
コマンドを実行してインストールします。
{ "require": { "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*" } }
これからは、autoload.phpの代わりにComposerによって生成されたオートローダーを使用します。 autoload.phpファイルを削除し、 front.phpでそのファイルへのリンクを変更します。
<?php // example.com/web/front.php require_once __DIR__.'/../vendor/.composer/autoload.php'; // ...
ルーティングコンポーネントは、一致の配列(リンクマップ)の代わりに、 「RouteCollection」クラスのインスタンスを使用します。
use Symfony\Component\Routing\RouteCollection; $routes = new RouteCollection();
/ hello / SOMETHINGのようなリンクのルートと、単純な/ byeのルートを追加しましょう。
use Symfony\Component\Routing\Route; $routes->add('hello', new Route('/hello/{name}', array('name' => 'World'))); $routes->add('bye', new Route('/bye'));
コレクションの各要素は、名前(hello)とRouteクラスのインスタンスによって定義されます。これは、ルートパターン
(/hello/{name})
とデフォルト属性の
(array('name' => 'World'))
によって決定されます。
このコンポーネントの公式ドキュメントには、URLの生成、属性要件、HTTPメソッドに従う、YAMLまたはXMLファイルの読み込み、パフォーマンスを向上させるために書き換えルールをPHPまたはApacheにエクスポートするなど、他の機能に関する情報が記載されています。
RouteCollectionクラスのインスタンスの情報に基づいて、 UrlMatcherクラスのインスタンスがパスをバインドします。
use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Matcher\UrlMatcher; $context = new RequestContext(); $context->fromRequest($request); $matcher = new UrlMatcher($routes, $context); $attributes = $matcher->match($request->getPathInfo());
match()メソッドは、要求されたパスを配列に分割します(一致するパスは_route特殊キーの下に自動的に保存されることに注意してください):
print_r($matcher->match('/bye')); array ( '_route' => 'bye', ); print_r($matcher->match('/hello/Fabien')); array ( 'name' => 'Fabien', '_route' => 'hello', ); print_r($matcher->match('/hello')); array ( 'name' => 'World', '_route' => 'hello', );
この例で要求パラメーターが不要な場合でも、メソッドの要件などを確認するために実際のプロジェクトで使用されます。
パスと一致するものが見つからない場合、例外がスローされます。
$matcher->match('/not-found'); // throws a Symfony\Component\Routing\Exception\ResourceNotFoundException
ここで、習得した知識を使用して、次のバージョンのフレームワークを作成します。
<?php // example.com/web/front.php require_once __DIR__.'/../vendor/.composer/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing; $request = Request::createFromGlobals(); $routes = include __DIR__.'/../src/app.php'; $context = new Routing\RequestContext(); $context->fromRequest($request); $matcher = new Routing\Matcher\UrlMatcher($routes, $context); try { extract($matcher->match($request->getPathInfo()), EXTR_SKIP); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); $response = new Response(ob_get_clean()); } catch (Routing\Exception\ResourceNotFoundException $e) { $response = new Response('Not Found', 404); } catch (Exception $e) { $response = new Response('An error occurred', 500); } $response->send();
新しいコードの変更:
•テンプレート名にはパス名が使用されます。
•エラー500は正しく処理されます
•テンプレートをシンプルに保つためにクエリパラメータが分離
<!-- example.com/src/pages/hello.php --> Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>
•ルート構成が別のファイルに移動されました。
<?php // example.com/src/app.php use Symfony\Component\Routing; $routes = new Routing\RouteCollection(); $routes->add('hello', new Routing\Route('/hello/{name}', array('name' => 'World'))); $routes->add('bye', new Routing\Route('/bye')); return $routes;
構成(すべて、アプリケーションの特性-app.php )とフレームワーク(アプリケーションのベースとなる残りのコード-front.php )を完全に分離しました。
約30行のコードを追加して、新しいフレームワークを取得しました。以前のフレームワークよりも生産的で柔軟性があります。 お楽しみください!
ルーティングコンポーネントを使用することには、もう1つの大きな利点があります。ルート定義に基づいてURLを生成する機能です。 コードでURLマッチングとURL生成を同時に使用する場合、URLの外観を変更しても影響はありません。 ジェネレーターの使用方法を学びたいですか? 非常に簡単:
use Symfony\Component\Routing; $generator = new Routing\Generator\UrlGenerator($routes, $context); echo $generator->generate('hello', array('name' => 'Fabien')); // outputs /hello/Fabien
コードは完全に明確であり、3番目のパラメーターtrueを追加することで絶対パスを生成することもできます。
echo $generator->generate('hello', array('name' => 'Fabien'), true); // outputs something like http://example.com/somewhere/hello/Fabien
パフォーマンスが心配ですか? ルート定義を使用して、デフォルトのUrlMatcherを置き換える最適化されたURLマッチクラスを作成します。
$dumper = new Routing\Matcher\Dumper\PhpMatcherDumper($routes); echo $dumper->dump();
さらにパフォーマンスが必要ですか? Apacheのmod_rewriteルールとしてルートをエクスポートします。
$dumper = new Routing\Matcher\Dumper\ApacheMatcherDumper($routes); echo $dumper->dump();
あいまいなユーザーと私は、このサイクルを一緒に翻訳することに同意しました。