Symfony2のデバッグモードのエラー画面で、ファイルのパスを物理ファイルにリンクする

app/config/config_dev.ymlに次のように記述すると、リンクになります。

app.config:
  ide: macvim

「macvim」だと「mvim://open〜」というURLでリンクされ、「textmate」だと「txmt://open〜」というURLになります。他の値を指定した場合、その値がファイルリンク用のパターンになります。

<?php
if (isset($config['ide'])) {
    switch ($config['ide']) {
        case 'textmate':
            $pattern = 'txmt://open?url=file://%%f&line=%%l';
            break;

        case 'macvim':
            $pattern = 'mvim://open?url=file://%%f&line=%%l';
            break;

        default:
            // should be the link pattern then
            $pattern = $config['ide'];
    }
}

config_dev.ymlではなくて、config.ymlにideの設定を記述するとうまく動かないのは、いまいち理由が分かっていませんが・・・。

Symfony2のconfigファイルにおけるimportsの挙動(DIのconfig)

Symfony2のconfigファイルでは、別のconfigファイルをインポートすることができます。
たとえばconfig_dev.ymlでは、共通設定であるconfig.ymlを以下のようにインポートしています。

imports:
    - { resource: config.yml }


また、config_dev.ymlをよく見ると、ルーティングの設定についても似たような記述があることが分かります。

app.config:
    router:   { resource: "%kernel.root_dir%/config/routing_dev.yml" }


前者のimportsで記述したresourceは、このconfigファイルの処理時点で同時にインポートされ、マージされて1つのconfigとして扱われます。
このimportsキーは、YAMLのルート階層にのみ記述できます。
そして、YAMLファイルがパースされるとき、まず最初にimportsの処理が行われ、インポートしているファイルから先に読み込まれて行きます。
この処理は、DependencyInjectionのYamlFileLoaderにおける共通処理となっています。

後者のrouterキーでのresourceは、FrameworkExtensionでの処理になっています。FrameworkExtensionのconfigLoadで、単にresourceのパスなどがコンテナの設定値として保存されます。(コンテナ・エクステンションの読み込み段階ではこれ以上の処理は行われない)

実際にrouterキーのresource(つまりrouting.yml)が読み込まれるのは、内部の処理が先に進み、RouterからRouteCollectionのloadが呼び出される時点です。ここでは、Routingコンポーネントが持つローダー(YamlFileLoader)で処理が行われるので、DIコンテナのconfigとは若干ルールが異なることに注意が必要です。

KernelとHttpKernel、その辺の起動順序

Symfony2で名前的に混乱しやすそうなのがKernelとHttpKernelで、

  • Kernelはアプリケーション全体のベースとなる
  • HttpKernelは1つのHTTPリクエストのベースとなる

という感じで、Kernelの方がベースよりです。これは説明するまでもないかもしれませんが、フロントコントローラーで一番最初に作られるAppKernelがKernelの方を継承しています。

で、ポイントはHttpKernelの初期化のあたりですが、これはDIコンテナ経由で行われます。
(※DIコンテナ自身はKernelが作ります)
Kernel::handle()内部で、

<?php
return $this->container->getHttpKernelService()->handle($request, $type, $raw);

とコンテナ経由でHttpKernelサービスにアクセスしているので、この時点でHttpKernelが初期化されます。

DIコンテナの面白い(すごい)のはここからなんですが、このHttpKernelを初期化する際に、同時にいくつかの他のサービスも初期化されます。なぜかというと、DIの定義で、HttpKernelのコンストラクタにいくつかのパラメーターが渡されていて、このパラメーターはDIが渡すのですが、そのパラメーターがまだ初期化されていなければ、それもまたその時点でインスタンス化されるからです。
具体的には、HttpKernelのコンストラクタには次の3つの引数があります。

  • ContainerInterface $container
  • EventDispatcher $dispatcher
  • ControllerResolverInterface $resolver

対応するDIの定義は、FrameworkBundleのservices.xmlにあります。

<service id="http_kernel" class="%http_kernel.class%">
    <argument type="service" id="service_container" />
    <argument type="service" id="event_dispatcher" />
    <argument type="service" id="controller_resolver" />
</service>

なので、HttpKernelがインスタンス化されると同時に、DIコンテナによってEventDispatcherがインスタンス化されます。


symfony 1的に「new EventDispatcher」でgrepしたりしても見つかりませんw
このあたりに慣れるまでは、ちょっとソースを追うのが大変かもしれません。