Symfony2に基づいて独自のフレームワークを作成します。 (パート6)

シリーズの第6部では、HttpKernelコンポーネントの使用方法を学習します。









私たちのフレームワークはすでにかなり全体的であるように思えますが、ある程度は改善されていますが、改善できる点を見てみましょう。

現時点では、すべての例で手続き型コードを使用していますが、コントローラーは静的クラスメソッドを含むオブジェクトメソッドを使用できることを覚えています。 それでは、コントローラーをクラスに変えましょう。



class LeapYearController { public function indexAction($request) { if (is_leap_year($request->attributes->get('year'))) { return new Response('Yep, this is a leap year!'); } return new Response('Nope, this is not a leap year.'); } }
      
      







それに応じてルート定義を更新します。



 $routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array( 'year' => null, '_controller' => array(new LeapYearController(), 'indexAction'), )));
      
      







この手順は非常に簡単で、ページの追加を開始するとすぐに意味があります。 確かに 、要求されたURLがleap_yearルートと一致しない場合でも、 LeapYearControllerクラス常に作成されることがすぐにわかります。 これは、パフォーマンスの観点からは非常に実用的ではありません。現在、すべてのルートのすべてのコントローラーは、リクエストごとに初期化されます。 遅延初期化を使用すると、対応するルートの「オンデマンド」でコントローラーの初期化が実行されます。

この問題や他の多くの問題を解決するには、 HttpKernelコンポーネントをインストールしましょう。



 { "require": { "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*", "symfony/http-kernel": "2.1.*" } }
      
      







HttpKernelコンポーネントには多くの興味深い機能がありますが、現時点ではコントローラーリゾルバーが必要です。 コントローラがいつ、どのように実行されるか、どの引数を渡すかを決定します。 すべてのコントローラーリゾルバーは次のインターフェイスを使用します



 namespace Symfony\Component\HttpKernel\Controller; interface ControllerResolverInterface { function getController(Request $request); function getArguments(Request $request, $controller); }
      
      







GetController()メソッドは、以前に採用したのと同じ規則に基づいています。_controllerには、リクエストに関連付けられたコントローラが含まれている必要があります。 PHPに組み込まれたコールバック関数と同様に、 getController()は、クラス名と2つのコロン、およびメソッド名-「class :: method」で構成される文字列を受け入れます。



 $routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array( 'year' => null, '_controller' => 'LeapYearController::indexAction', )));
      
      







このコードを機能させるには、フレームワークコードを変更して、 HttpKernelの コントローラーリゾルバーを使用します



 use Symfony\Component\HttpKernel; $resolver = new HttpKernel\Controller\ControllerResolver(); $controller = $resolver->getController($request); $arguments = $resolver->getArguments($request, $controller); $response = call_user_func_array($controller, $arguments);
      
      







楽しい些細なこと:コントローラーリゾルバーはエラーを正しく処理します。たとえば、_controllerの定義を忘れると、適切なエラーが発生します。




ここで、コントローラーの引数がどのように認識されるかを見てみましょう。 getArguments()は、コントローラのシグネチャを解析して、PHP Reflectionのリバースエンジニアリングを使用して、渡す引数を決定します。

IndexAction()メソッドの引数は、 Requestクラスのオブジェクトです。 型が正しく定義されている場合、 getArguments()メソッドは引数を正しく渡します。



 public function indexAction(Request $request) // won't work public function indexAction($request)
      
      







興味深い事実は、 Requestクラスの属性をgetArguments()に渡すこともできるということです。 引数に対応する属性と同じ名前を付けるだけで十分です。



 public function indexAction($year)
      
      







Requestクラスのオブジェクトで異なる属性を同時に渡すこともできます(タイプは指定されているため、順序は重要ではありません)



 public function indexAction(Request $request, $year) public function indexAction($year, Request $request)
      
      







当然、任意の引数にデフォルト値を定義できます。



 public function indexAction($year = 2012)
      
      







$ yearをコントローラーに渡してみましょう:



 class LeapYearController { public function indexAction($year) { if (is_leap_year($year)) { return new Response('Yep, this is a leap year!'); } return new Response('Nope, this is not a leap year.'); } }
      
      







コントローラーリゾルバーは、コントローラーの呼び出しとその引数のチェックも行います。 問題が発生した場合、問題を説明する明確なメッセージとともに例外をスローします。クラスコントローラーが存在しない、このメソッドが定義されていない、引数が属性と一致しない、...

フレームワークの新しいバージョンを要約するには:



 <?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; use Symfony\Component\HttpKernel; function render_template($request) { extract($request->attributes->all()); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); return new Response(ob_get_clean()); } $request = Request::createFromGlobals(); $routes = include __DIR__.'/../src/app.php'; $context = new Routing\RequestContext(); $context->fromRequest($request); $matcher = new Routing\Matcher\UrlMatcher($routes, $context); $resolver = new HttpKernel\Controller\ControllerResolver(); try { $request->attributes->add($matcher->match($request->getPathInfo())); $controller = $resolver->getController($request); $arguments = $resolver->getArguments($request, $controller); $response = call_user_func_array($controller, $arguments); } catch (Routing\Exception\ResourceNotFoundException $e) { $response = new Response('Not Found', 404); } catch (Exception $e) { $response = new Response('An error occurred', 500); } $response->send();
      
      







ちょっと考えてみてください。私たちのフレームワークはさらに信頼性が高く柔軟になり、40行未満のコードで構成されています



All Articles