プログラミング - THIS IS IT !

より良い開発をすべく日々奮闘しているプログラマーのブログです。設計に興味があります。主にPHPネタを書いてます 日本Symfonyユーザー会

Symfonyでダイナミックにconfigリソースを読み込む方法

f:id:okapon_pon:20151205123050p:plain

この記事はSymfony Advent Calender 2016 7日目の記事です。

実は以前Symfony meetupのLTで話した内容(スライド作ってない)ですが、改めてご紹介してみたいと思います。


ここから少し宣伝

宣伝となりますがSymfony meetup ではLT発表枠がありまして、そこではネット上に(特に日本語では)あまり公開されてないノウハウなどが聞くことができるかもしれません。

↓興味がありましたら、meetupの方への参加もよろしくお願いします。

symfony.connpass.com

宣伝終わり


では本題

Symfonyのconfigリソース

Symfonyでは config.yml などの設定ファイルをresourceと呼んでいます。 assetやview、translationなどもresourceと呼びますがここではconfigリソースについて解説します。

Symfonyのコンフィグレーションドキュメント Configuring Symfony (and Environments) (Symfony Docs)

上記ドキュメントから引用&抜粋

Configuration Formats

Throughout the chapters, all configuration examples will be shown in three formats (YAML, XML and PHP). YAML is used by default, but you can choose whatever you like best. There is no performance difference:

The YAML Format: Simple, clean and readable; XML: More powerful than YAML at times & supports IDE autocompletion; PHP: Very powerful but less readable than standard configuration formats.

You can also load XML files or PHP files.

ドキュメントに記載の通り、Symfonyの設定ファイルはyml以外にもxmlphpファイル、またiniファイル(下記リンク参照)も読み込むことが可能です。

【参考】

Loading Resources (The Config Component - Symfony Docs)

本記事で扱う内容

説明にもある通りymlはシンプルに書けるし、symfonyの標準フォーマットでもあるので、基本的にyamlで書くことがほとんどだと思いますが、ここではphpフォーマットについて解説したいと思います。

phpフォーマットの場合なconfigを設定できる点が他のフォーマットと異なります。xmlやiniとyamlの違いはあくまでフォーマットの違いでしかなく、静的な設定ファイルという点においては何も変わりません。

phpファイルをリソースとして読み込む方法

例えば、コンテナにパラメータを設定したい場合には以下のように書けます。

# app/config/config.yml
imports:
    - { resource: 'parameters.php' }
<?php

$container->setParameter('key', 'value');

これだけではあまりに簡単過ぎて「yamlで十分じゃないか」という話になってしまうので、これからphpファイルををリソースとして読み込むことのメリットを解説したいと思います。

phpファイルをリソースとして利用するメリット

yamlではできない動的(ダイナミック)な処理を行うことができる

これに尽きます。

phpファイルであるため、パラメーターの設定や置き換え、特定条件下で実行するなどどんな処理でも行うことができます。

いくつか実例

基本編

1. 環境変数から値を取得してパラメーターをセットする

サービスコンテナで外部パラメータをセットする方法 | Symfony2日本語ドキュメント

Symfony は、 SYMFONY__ の接頭辞の付いたあらゆる環境変数を読み取り、サービスコンテナのパラメータとしてセットすることができます。

こちらのドキュメントにも書いてありますが、元々の機能として SYMFONY__ というprefixがついた環境変数は自動でパラメーターとして設定されます。

しかし、環境変数がこういった命名規則に則っていない場合もあると思います。(複数アプリで同じ値を読まないといけない場合など) その場合もphpなら値を入れられますね。

この方法を用いれば環境変数以外にも外部から取得してきた値を利用することも可能です。

2. 特定ファイルが存在する時にだけ設定を読み込む

<?php
$file = __DIR__ . '/some_file.php';

if (file_exists($file)) {
    // overrideしたいパラメーターをセット
    $params = require $file;
    foreach ($params as $key => $value) {
        $container->setParameter($key, $value);
    }
}

単に設定がphpファイルで書かれている場合にも利用できます。 あまり実用的ではないですが、レガシーコードに依存している場合などどうしようもない状況下では選択肢としてはあり得るのかなと。

※ dev環境では通常コードを書き換えたらコンテナは再生されますが some_file.php を更新してもファイルの変更が検知されるcacheが再生成されないため注意

応用編

これまで parameterの設定にのみ絞って紹介しましたが、ここからは動的にconfigrationする方法を紹介します。

ここまでちゃんと説明してこなかったのですが、実はリソースファイルの読み込みはcontainerのコンパイル前に行われます。 そのため、リソースファイルに書いた設定でframeworkの動作を変更することもできます。 またコンパイルされてキャッシュされるということは、毎回ファイルを読み込むこともないのでパフォーマンス面も気にする必要はありません。

では、引き続き実例を紹介します。

3. 特定環境下でDBのconnectionを変えたい

開発サーバー上とlocalhost上で接続先を変えたい場合など

<?php
// どこかから設定を取ってくる
$connection = [
    'host' => 'localhost',
    'dbname' => 'blog_local',
    'user' => 'root',
    'password' => null,
];
$container->loadFromExtension('doctrine', [
    'dbal' => [
        'connections' => [
                'default' => $connection,
            ],
        ],
    ],
]);

少し応用すれば、DB名にtest_ prefixを付けたり、動的にslaveサーバーのhostを追加することもできます。

4. 自動でassetバージョンを付与する

これが今回紹介する中で一番簡単で実用的かもしれません。

assetバージョンとは?

<link rel="stylesheet" href="/asset/css/style.css?201612070000">

こういうやつです。 cssファイルなどの静的コンテンツにクエリーストリングとして、バージョン番号を付けてやることで、cssを更新した際に古いcssがブラウザにcacheされ続けてしまう問題を回避することができます。

<?php
// app/config/assets.php
$container->loadFromExtension('framework', [
    'assets' => [
        'version' => date('YmdHi'),
    ],
]);

phpリソースファイル中でコンテナが使えるのはなぜ?

リソースファイルが読み込まれる箇所

Kernel.php https://github.com/symfony/symfony/blob/a4edafbd7d82ccea983f04ade3dc220e72f340e2/src/Symfony/Component/HttpKernel/Kernel.php#L659

PhpFIleLoader https://github.com/symfony/symfony/blob/a4edafbd7d82ccea983f04ade3dc220e72f340e2/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php#L39

<?php
    public function load($resource, $type = null)
    {
        // the container and loader variables are exposed to the included file below
        $container = $this->container;
        $loader = $this;

        $path = $this->locator->locate($resource);
        $this->setCurrentDir(dirname($path));
        $this->container->addResource(new FileResource($path));

        include $path; // ここでphpファイルを読み込む
    }

load()メソッド内に$containerというローカル変数を作っているので phpのリソース中から$containerが参照できるのでした。

まとめ

  • configファイルにphpを利用したら黒魔術的ではあるがわりと何でも設定できる。
  • ただし、やり過ぎると何が設定されているか分からなくなってしまうので注意