somemo's diary

プログラマ、雑記、プログラミング関係はLinkから、数式はこっちでまとめていることが多い

【Symfony2】test時のenviromentとconfig.yml

testであることは分かっているのですが、どのようにして決まるのかを調べてみました。

clientの作成

テスト実行の際には、直接Kernelを呼ばないので直に環境指定をしていません。ブラウザから実行する際には、web配下のapp.phpまたはapp_dev.phpがフロントコントローラーとなるため下記コードがまず実行されます。

$kernel = new AppKernel('prod', false);

テスト実行の際にはどこで指定するかというと、clientを作成するコードが基点となります。

$client = static::createClient();

作成の過程

テストコードで必ず書いているSymfony2提供のWebTestCaseを見ていきます。

abstract class WebTestCase extends \PHPUnit_Framework_TestCase

カーネルの作成と起動をしています。起動したカーネルからコンテナ経由でテスト用のクライアントを取得しています。カーネル作成時のオプションを渡すこともできますが、何も渡さずともtest環境になります。

static protected function createClient(array $options = array(), array $server = array())
{
    static::$kernel = static::createKernel($options);
    static::$kernel->boot();

    $client = static::$kernel->getContainer()->get('test.client');
    $client->setServerParameters($server);

    return $client;
}

カーネルの取得をするために、クラス名を決定しています。クラス名が決まった後が今回見たかった部分です。option配列にenviroment指定をしている場合はそれを、指定していない場合はtestとなります。また、デフォルトですと自動でdebugモードになります。

static protected function createKernel(array $options = array())
{
    if (null === static::$class) {
        static::$class = static::getKernelClass();
    }

    return new static::$class(
        isset($options['environment']) ? $options['environment'] : 'test',
        isset($options['debug']) ? $options['debug'] : true
    );
}

まず、カーネルの存在するディレクトリを取得しています。テスト実行の場合、以前にapp/bootstrap.php.cacheで指定したサーバ変数がありますので、その値になります。これをもとに、Symfony2提供のfinderコンポーネントを用いてカーネルのクラス名を取得します。

static protected function getKernelClass()
{
    $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : static::getPhpUnitXmlDir();

    $finder = new Finder(); // Symfony\\Component\\Finder\\Finder
    $finder->name('*Kernel.php')->depth(0)->in($dir);
    $results = iterator_to_array($finder); // array[1] Symfony\\Component\\Finder\\SplFileInfo
    if (!count($results)) {
        throw new \RuntimeException('Either set KERNEL_DIR in your phpunit.xml according to http://symfony.com/doc/current/book/testing.html#your-first-functional-test or override the WebTestCase::createKernel() method.');
    }

    $file = current($results);
    $class = $file->getBasename('.php');// AppKernel

    require_once $file;

    return $class;
}

createKernelメソッドに戻り、クラス名を指定してインスタンス生成を行います。ただし生成しているのは、appKernelです。コンストラクタでenvironmentがセットされます。

abstract class Kernel implements KernelInterface
{
    public function __construct($environment, $debug)
    {
        $this->environment = $environment;
        $this->debug = (Boolean) $debug;
        $this->booted = false;
        $this->rootDir = $this->getRootDir();
        $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
        $this->classes = array();

        if ($this->debug) {
            $this->startTime = microtime(true);
        }

        $this->init();
    }
}

カーネルインスタンス生成が完了後、bootメソッドによりバンドルとDIコンテナの初期化をします。コンテナの初期化中には、バンドルをコンテナに入れ、コンテナ用の設定を読み込んでいます。

public function registerContainerConfiguration(LoaderInterface $loader)
{
    $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}

ここでymlを読み込み、設定が反映されているようです。ブラウザからアクセスした場合、bootメソッドはカーネルのhandleメソッド内で実行されます。その後、カーネルのhttp実装に処理が移りルーティングなどを行っていきます。testの場合、リクエストをカーネルに通すわけではないので異なっています。

config.yml

config.ymlには以下の4つがあります。

  • config.yml
  • config_dev.yml
  • config_prod.yml
  • config_test.yml

各ymlには、importsというキーが存在していて文字通りファイルを読み込むつくりになっています。それぞれのymlが読み込む対象は以下のとおりです。

imports:
    - { resource: parameters.ini }
    - { resource: security.yml }
imports:
    - { resource: config.yml }
imports:
    - { resource: config.yml }
imports:
    - { resource: config_dev.yml }

testの場合、devを読み込みます。devとprodは、最終的にconfig.ymlを読み込むようになっています。そのconfig.ymlはDBなどの各パラメータとセキュリティに関する設定を読み込みます。