[Symfony2]Symfony2 Deep Tour 1

Symfony2の内部構造はまだまだよく分かっていないので、Requestが生成されてからResponseとして返されるまでの一連の流れを細かく追っていこうかと思いました。そんなわけでタイトルにDeepなんて付いてるわけですが、現時点でSymfony2を使おうとする人なんて強者ばかりなんじゃないかと思って、そういう人達にとっては全然Deepじゃないかもしれません。そしてちゃんとこの企画を続けられるかはわかりませんww

基準となるソースコードは、2010/11/18 02:00:00時点のSymfony-sandboxとします。

長くなりそうなんで、何回かに分けて投稿します。小見出しの [@/〜/〜]っていうのは、その小見出しがどのファイルの中の処理について議論しているかを表しています。当然奥深くのファイルに入って、入って、戻って、戻ってみたいな感じになるので、そのうち分かりやすく図示できるといいですね。なお、最初の方の説明は、Quick TourのThe Architecture章にも解説があったりします。

フロントコントローラの実行 [@/web/app_dev.php]

Symfony2のフロントコントローラはapp.phpとするのが慣例になりそうです。app.phpは”prod”環境(environment)用、それ以外の_xxxが付くフロントコントローラは”xxx”環境用にするのが慣例です。sandboxを見ると、prod環境のapp.phpとdev環境のapp_dev.phpの2つが予めあると思います。

まずはSymfony2の中核となる/app/AppKernel.phpをインクルードします。

オートローダーの読み込み [@/app/AppKernel.php]

AppKernel.phpはKernelクラスを継承したクラスで、アプリケーションの中核となりますが、その冒頭ではオートローダーを読み込むために/src/autoload.phpをインクルードしています。

オートローダの設定 [@/src/autoload.php]

Symfony2のオートローダは、UniversalClassLoaderクラス(Symfony\Component\HttpFoundation\UniversalClassLoader)が担います。このクラスはPHP 5.3用の普遍的なオートローダとして実装していて、PHP 5.3の名前空間やクラス名の相互運用のための技術標準(PSR-0-Final)の形式や、PEAR形式のクラスの両方で使用可能になっています。

autoload.phpではまず、UniversalClassLoaderのインスタンス$loaderを生成し、主要なクラス群の名前空間とフォルダパスの対応関係を登録しています。最後に、$loader::loadClass()メソッドを、__autoload()マジックメソッドの実装として登録します。これ以降はプログラム内で存在していないクラスを読み込もうとすると、__autoload()メソッドの代わりとして$loader::loadClass()メソッドが呼ばれるように成ります。

オートロードが必要なライブラリ群が増えた場合は、このファイルに追記する必要があるかも知れません。Symfony2では、/srcディレクトリ以下に格納されているすべてのファイルの責任をこのautoload.phpが担うものと決められています。

AppKernelインスタンスの生成 [@/web/app_dev.php]

2つのファイルをインクルードした後はまたフロントコントローラに戻り、AppKernelクラスのインスタンス$kernelを生成します。AppKernelはKernelクラス(Symfony\Component\HttpKernel\Kernel)を継承しています。KernelはSymfony2のシステムの心臓とも言えるべきクラスで、ある1つの環境(environment)を管理し、様々なバンドルを取り扱うことができます。

Kernel::__construct()が呼ばれます。第1引数は環境名(prod, dev, …)、第2引数はデバッグフラグです。デバッグフラグがtureのときは自動的にdisplay_errors設定がOnになり、error_reporting設定が-1になり、実行時間を計測し始めます。-1ということは2進数的にはすべてのビットが立っているので、仮に新しいエラー定数が増えても自動的に対応できます。逆にデバッグフラグがfalseのときは自動的にdisplay_errors設定がOffになります。

Kernelクラスは抽象クラスなので、registerRootDir()、registerBundles()、registerBundleDirs()、registercontainerConfiguration()の4つのメソッドをAppKernelで定義する必要があります。

Kernelの実行 [@/web/app_dev.php]

Kernelを実行するために、Kernel::handle()メソッドを実行します。第1引数にはRequestクラス(Symfony\Component\HttpFoundation\Request)を与えます。返り値はResponseクラス(Symfony\Component\HttpFoundation\Response)が返ってきます。

Requestクラスは、ある1つのHTTPリクエストそのものをオブジェクトとして表しているものです。クラスの内部にスーパーグローバル変数($_GETや$_POST、$_COOKIE、$FILESなど)から取得した値を持っていたり、他にもHTTPヘッダに含まれている情報などをひとまとまりにして保持しています。

Responseクラスは、ある1つのHTTPリクエストの結果(HTTPレスポンス)そのものをオブジェクトとして表しているもの(のはず)です。基本的にはHTTPヘッダ、HTTPステータス、レスポンス本文を持っているだけです。

このようにSymfony2では、実際のHTTPの概念とオブジェクトが1対1対応しているので、非常に分かりやすい構成と言えます。実際にRequestオブジェクトをResponseオブジェクトに変換する処理を担うのは、後述するControllerという概念に成ります。

捕捉ですが、Kernelは内部的に名前$this->nameを持っていて、AppKernel.phpのフォルダ名(例:app)が自動的に採用されます。この名前の概念は内部的にしか使われませんが、DIコンテナのクラス名の一部に使用されたりするので、覚えておくといいかも知れません。

今回はここまで。次回はDIコンテナが主役です。

About: uechoco