Zend_Configでグローバルセッティング

フロントコントローラーに安全かつ柔軟な設定レジストリを作ってみました。

上書き禁止なfrontControllerのZend_Config

フロントコントローラーを拡張して、ReadOnlyなZend_Configオブジェクトを持たせます。
たとえば、フロントコントローラーで

<?php
    protected $_config;
    public function getConfig()
    {
        return $this->_config;
    }
    public function setConfig(Zend_Config $config)
    {
        if (isset($this->_config)) {
            throw new Zend_Config_Exception('[frontCtrl] don\'t try to override readonly config');
        } else {
            $this->_config = $config;
        }
        return $this;
    }
    public function loadConfig($path, $section, $options, $configClass = 'Zend_Config_Ini')
    {
        $ref = new ReflectionClass($configClass);
        $config = $ref->newInstance($path, $section, $options);
        if (! $config instanceof Zend_Config) {
            return false;
        }
        if (isset($this->_config) && $this->_config->readOnly()) {
            throw new Zend_Controller_Exception('[frontCtrl] config is readOnly');
        }
        if ($this->_config instanceof Zend_Config) {
            try {
                $this->_config->merge($config);
            } catch (Zend_Config_Exception $e) {
                $this->log($e->getMessage(), Zend_Log::NOTICE);
                return false;
            }
        } else {
            $this->_config = $config;
        }
        return $this->_config;
    }

これで、初期ロード時にreadOnlyで作成すれば上書きできない設定情報を作成・保持できます。

もう少し柔軟に

一度しか読み込めないと、グローバルな設定情報を同じファイルに大量に書いておかないと使えません。不便ですね。そこで、最初は大元のコンフィグをallowModification = true、書き込み可で作成します。
次に別のコンフィグファイルに書いた内容等を明示的にかつ部分的にmergeしていきます。
たとえば、モジュール毎の設定があったなら、modules以下にマージするとしたら、

<?php
$moduleConf = Zend_Config(array('test'=> 'hoge'));
Zend_Controller_Front::getInstance()->getConfig()->modules->merge($moduleConf);

これで、個別モジュールの情報を追加保存できます。
データベース接続その他、上書きされたなくない情報については、
$config->globaldb->setReadOnly()をコールしてそのコンフィグを読み込み専用にしたいところ。
たとえば、一部だけを遅延ロードするなら、こんな感じ。setReadOnlyRecursivelyについては後述。

<?php
        $config->rewind();
        do {
            if ($config->current() instanceof Zend_Config && $config->key() !== 'modules') {
                $config->current()->setReadOnlyRecursively();
            }
            $config->next();
        } while($config->valid());

Zend_Configにも多少の工夫が必要

http://framework.zend.com/issues/browse/ZF-4728
こちらにもある通り、setReadOnlyは現在階層に対してしか効果がありません。そこで、再帰的にreadOnlyにしたい場合は、ZF-4728とは別にsetReadOnlyRecursivelyを作って、

<?php
    public function setReadOnlyRecursively()
    {
        $this->setReadOnly();
        foreach ($this as $value) {
            if ($value instanceof Zend_Config) {
                $value->setReadOnlyRecursively();
            }
        }
    }

readOnlyは現階層のみ、configの中をseekして必要な階層以下に対して、setReadOnlyRecursivelyにすることで、柔軟かつ安全なコンフィグを保存できるようになりました。
Zend Frameworkの将来のバージョンでsetReadOnlyの仕様が変わったとしたら、変更が必要になります。