|
|
||
この記事は、Symfony アドベントカレンダー 2010 に参加しています。
ごく最近にプレビューリリース4が出たSymfony2ですが、最近になってSymfony2勉強会等に参加したりid:Fivestarから話を聞いたりしてしてようやくSymfony2を触るようになりました。個人的にはSymfony2の最も大きな特徴はDIコンテナとバンドルという概念が導入されていることではないかと思います。 symfony1系からあるEventDispatcherに加え、Symfony2でDIコンテナが追加されたことにより、Symfony2のフレームワークとしての柔軟性、アプリケーションの設計のしやすさはより上がっています。また、バンドルという概念が導入されたことによりSymfony2ユーザコミュニティによって再利用可能なものが多くなるという期待も持てます。
この記事では、HelloWorldバンドルというごく単純なバンドルを解説していきます。 単純なバンドルですが、Symfony2フレームワークのルーティング処理などを吹き飛ばして強制的にhello worldと表示するというものです。 このHelloWorldバンドルがどのようにしてフレームワークのコアの処理を改変しているのかを実際のコードと共に説明していきたいと思います。
まず、バンドルの説明をします。
Symfony2でDIコンテナと共に新しく導入されたバンドルは、いわばsymfony1のプラグインのように他のアプリケーションに組み込まれることを前提としたモジュールのようなものです。一度作成されたバンドルは、他のアプリケーションから利用することができます。ただし、プラグインと違う点としてあるのは、アプリケーションを書く際にもバンドルを書く必要があるということです。というのもSymfony2では何もかもをバンドルとして実装する仕組みになっているからです。
バンドルは、コンソールで使うコマンド、DIコンテナ設定、コントローラ、ルーティング設定、テンプレート、ライブラリなど様々なものを持つことができます。Symfony2フレームワークでアプリケーションを開発する際には、アプリケーションに必要なバンドルを組み合わせつつアプリケーション自身のバンドルを開発することになります。
Symfony2では、フレームワークのコア機能郡ですらもSymfony\Bundle\FrameworkBundleとして提供されています。
何もかもがバンドルとして実装することで、再利用しやすいコードを増やしていくのがSymfony2の戦略のようです。 実際にDiscover 1140 bundles for Symfony2 | KnpBundlesではすでにバンドルがいくつも公開されており、それらを利用することができます。
それではHelloWorldバンドルを説明していきます。とても小さなバンドルなのでソースコードも全て載せていきます。
予めバンドルの流れを説明すると以下のようになります。
フレームワークのルーティング処理を横取りしてhello worldと表示するだけのバンドルです。
単純ですが、フレームワークのコアのコードをいじらずにフレームワークのコアの振る舞いを変えています。
Symfony2の実行環境としてPR4のサンドボックスが公開されていますのでこの記事ではそれを利用します。
HelloWorldバンドルのディレクトリのレイアウトを示します。
サンドボックスのsrcディレクトリに以下のようなレイアウトが展開されます。
Application/
`-- HelloWorldBundle
|-- DependencyInjection
| `-- HelloWorldExtension.php
|-- HelloWorld.php
`-- HelloWorldBundle.php
ファイル自体は三つしかありません。
Application\HelloWorldBundleクラスは、実行時にアプリケーションにバンドルとして追加するのに必要なインターフェイスを提供します。 とはいえ、このクラスではSymfony\Component\HttpKernel\Bundloe\Bundleクラスを継承しているだけです。
<?php namespace Application\HelloWorldBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; class HelloWorldBundle extends Bundle { }
単にバンドルとしてインポートするのに必要なファイルというだけで、このバンドルでは特になにかメソッドをオーバーライドする必要はありませんでした。
DependencyInjectionディレクトリ以下にはHelloWorldExtension.phpがあります。
このファイルはDIコンテナの設定を行うためのクラスをおさめたファイルで、エクステンションと呼ばれます。
アプリケーションがバンドルとして追加された際に、どんなバンドルでもDependencyInjectionディレクトリ以下の*Extension.phpというファイルはDIコンテナの設定をするものとして自動的に利用されます。
<?php namespace Application\HelloWorldBundle\DependencyInjection; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface, Symfony\Component\DependencyInjection\Definition, Symfony\Component\DependencyInjection\ContainerBuilder; class HelloWorldExtension implements ExtensionInterface { // 設定ファイルでのプレフィクス function getAlias() { return 'helloworld'; } function load($tag, array $config, ContainerBuilder $builder) { $def = new Definition('Application\HelloWorldBundle\HelloWorld', array()); $def->addTag('kernel.listener', array('priority' => 1)); // 'kernel.listener' タグの追加 return $builder->setDefinition('hook.listener', $def); } // 今回は不要 function getNamespace() { return null; } // 今回は不要 function getXsdValidationBasePath() { return null; } }
この中のloadメソッドでは、Application\HelloWorldBundle\HelloWorldクラスをDIコンテナに登録しています。
DIコンテナの設定には、XML, YAML, INIファイルなどでも行えるのですが、今回は簡単にPHPで直接設定しています。
Symfony2のDIコンテナでは、クラスを登録する際にタグを設定することができます。ここで設定された "kernel.listener" タグは、Symfony2フレームワークのコアのふるまいを司るイベントディスパッチャの設定をするオブジェクトのために用意されています。
Symfony2がリクエストからレスポンスを返す仕組みのすべてはこのイベントディスパッチャを通じて行われるため、このイベントディスパッチャをうまく設定することによってフレームワークのルーティング処理などを先に横取りすることができます。その具体的にな処理は次のHelloWorldクラスで行われます。
<?php namespace Application\HelloWorldBundle; use Symfony\Bundle\FrameworkBundle\EventDispatcher, Symfony\Component\EventDispatcher\Event, Symfony\Component\HttpFoundation\Response, Symfony\Component\HttpKernel\HttpKernelInterface; class HelloWorld { function register(EventDispatcher $dispatcher, $priority) { $dispatcher->connect('core.request', array($this, 'handleRequestEvent'), $priority); } /** * @param Symfony\Component\EventDispatcher\Event * @return null */ function handleRequestEvent(Event $event) { if ($event->get('request_type') === HttpKernelInterface::MASTER_REQUEST) { $event->get('request')->attributes->set('_controller' , __CLASS__ . '::hello'); $event->get('request')->attributes->set('hoge' ,'world'); } } /** * @return Symfony\Component\HttpFoundation\Response */ static function hello($hoge) { $response = new Response(); $response->setContent('hello ' . $hoge); return $response; } }
このクラスでは、イベントディスパッチャの設定とコントローラの決定、hello worldと返すコントローラという三つの処理を持っています。
registerメソッドは、DIコンテナに 'kernel.lister' というタグをつけて設定することでフレームワーク側から自動的に呼ばれるメソッドです。このメソッドの中では "core.request" というイベントが起きた時にhandleRequestEventというメソッドを呼び出すように設定しています。
handleRequestEventメソッドでは、ルーティング処理をする前に実行するコントローラを勝手に決める処理を記述します。 フレームワークのルーティング処理を横取りしているわけです。コントローラは強制的にhelloメソッドに決定されます。
Symfony2フレームワークは内部でコントローラを呼び出し、それが返すResponseオブジェクトを元にHTTPレスポンスを生成します。
これがルーティング処理を吹っ飛ばしてhello worldと表示するバンドルの正体です。
最後にアプリケーションで実際にこのHelloWorldバンドルを使う方法を解説します。
先ほどのPR4のサンドボックスのapp/AppKernel.phpにあるAppKernel::registerBundles()メソッドに以下のようにHelloWorldバンドルを追加します。
<?php public function registerBundles() { $bundles = array( new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), // enable third-party bundles new Symfony\Bundle\ZendBundle\ZendBundle(), new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), //new Symfony\Bundle\DoctrineMigrationsBundle\DoctrineMigrationsBundle(), //new Symfony\Bundle\DoctrineMongoDBBundle\DoctrineMongoDBBundle(), // HelloWorldBundleの追加 new Application\HelloWorldBundle\HelloWorldBundle(), ); if ($this->isDebug()) { $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); } return $bundles; }
次に app/config/config.yml に、HelloWorldExtensionを読み込むための設定を以下のように記述します。
app.config: charset: UTF-8 error_handler: null csrf_secret: xxxxxxxxxx router: { resource: "%kernel.root_dir%/config/routing.yml" } validation: { enabled: true, annotations: true } templating: {} #assets_version: SomeVersionScheme session: default_locale: en lifetime: 3600 auto_start: true # Twig Configuration twig.config: debug: %kernel.debug% strict_variables: %kernel.debug% helloworld.use: ~ # ここに追加
これでバンドルを利用するための設定は終わりです。
ブラウザから サンドボックスのwebディレクトリ以下にアクセスしてください。 ルーティング設定をいくらやっても必ずhello worldが表示されるアプリケーションの完成です。
というわけで単純なHelloWorldバンドルの解説をしました。
Symfony2ではDIコンテナの設定やイベントディスパッチャの設定によってフレームワークのコアの振る舞いも変えることができるということがわかると思います。 また、バンドルとして実装することで簡単に再利用可能なコードを生み出すことができるということもわかったと思います。
Symfony Advent 2010では12月1日から12月24日までを使って日替わりでsymfonyでイイなと思った小さなtipsから内部構造まで迫った解説などをブログ記事にし て公開していくイベントです。
参加についてはATNDで参加表明の上、Google GroupのSymfony Advent 2010に追加リクエストを送信ください。