2011
01.25

Symfony2は、登録された機能(バンドル)の情報を読み込み、結果ひとつのDIコンテナを生成します。

生成されたDIコンテナはcacheディレクトリの中に格納され、二回目以降は
このコンテナを参照するようになっています。
各サービスの情報を集約した結果がPHPのクラスとして出力されるのが
通常のパターンですが、ソースを見る限りでは違った出力方法も用意されているようです。

その中に、
Symfony\Component\DependencyInjection\Dumper\GraphvizDumper
というダンパーが存在します。

このクラスは、DIコンテナの中身を解析して、DOT言語を出力します。

これをGraphvizを使って画像に変換することで、DIコンテナの中身を
グラフィカルに表現することが可能になります。

とういわけで、さっそくsandboxのDIコンテナを画像に変換してみようと試みました。

あらかじめ、Graphvizがインストールされていることが前提条件となります。
Graphvizが存在しない場合は、以下のサイトからダウンロードすることができます。
http://www.graphviz.org/

当初は、ダンパーをPhpDumperからGraphvizDumperに交換してやることで、
容易に目的を達成できるかと思いましたが、フレームワーク内PhpDumperの使用箇所は変更できないみたいです。
仕方ないので、Actionの中に、ごりごりコードを書いていきます。
※ 環境は2011/01/23 時点のsandboxです。

GraphvizDumperのコンストラクタにはCotainerBuilderを与えてやる必要があるので、Kernelの
initializeContainerあたりを参考に、バンドルを登録し終えたContainerBuilderを再現していきます。

        $kernel = $this->get('kernel');
        $parameterBag = new ParameterBag($kernel->getKernelParameters());
                                                                                       
        $container = new ContainerBuilder($parameterBag);
        foreach ($kernel->getBundles() as $bundle) {
            $bundle->registerExtensions($container);  
        }
        $resolver = new LoaderResolver(array(
            new YamlFileLoader($container)
        ));
        $loader = new DelegatingLoader($resolver);
        $kernel->registerContainerConfiguration($loader);

この後コンパイルを行う必要がありますが、ContainerBuilder自身のcompileメソッドを実行すると
ParameterBagが凍結されてしまい、エラーになってしまいます。
ですので、Compilerクラスのcompileを利用します。

        $compiler = new Compiler();
        $compiler->compile($container);

GraphvizDumperのdump()で、dotファイルを吐き出します。

        $dumper = new GraphvizDumper($container);
        file_put_contents('/tmp/container.dot', $dumper->dump());

dump()メソッドにオプション指定することで、フォントの種類やサイズ、背景色なども
変更できたりします。GraphvizDumperのソースを見て頂けると、どのような
オプションが指定できるか確認できるかと思います。

これをGraphvizで、画像を生成します。

% dot -Tpng /tmp/container.dot > /path/to/container.png


実際の画像はこちら
※フォントサイズを若干いじっています。

各サービスが、どのサービスと依存しているのかが、矢印のつながりで把握できますね。
ただ、あまり細かいことは反映してくれないようです。

コマンドラインから画像生成してもいいのですが、せっかくなので
アクション内ですべて完結させてみました。

<?php

namespace Application\GraphvizBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\Dumper\GraphvizDumper;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DelegatingLoader;
use Symfony\Component\DependencyInjection\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Compiler\Compiler;
use Symfony\Component\HttpFoundation\Response;

class GraphvizController extends Controller
{
    public function indexAction()
    {
        $kernel = $this->get('kernel');
        $parameterBag = new ParameterBag($kernel->getKernelParameters());
        $container = new ContainerBuilder($parameterBag);
        foreach ($kernel->getBundles() as $bundle) {
            $bundle->registerExtensions($container);
        }
        $resolver = new LoaderResolver(array(
            new YamlFileLoader($container)  
        ));
        $loader = new DelegatingLoader($resolver);
        $kernel->registerContainerConfiguration($loader);
 
        $compiler = new Compiler();    
        $compiler->compile($container);

        $dumper = new GraphvizDumper($container);
        file_put_contents('/tmp/container.dot', $dumper->dump());
        exec('/usr/local/bin/dot -Tpng /tmp/container.dot > /tmp/container.png');
        $png = file_get_contents('/tmp/container.png');

        return new Response($png, 200, array('Content-Type'=>'image/png'));
    }
}

Symfony2が今後変更されると、上記のコードは動かなくなる可能性大ですのでご了承ください。

DIコンテナを画像で表示する事で幸せになれるかはさておき、
こんなこともできたよ、ということで。

No Comment.

Add Your Comment