config_handlers.ymlを書き換えるときの注意点
デバッグモードが無効な状態で、config_handlers.ymlに自前のコンフィグハンドラーを指定している場合、たぶんクラスが存在しないエラーがでちゃいますよ!ということです。
<?php abstract class sfApplicationConfiguration extends ProjectConfiguration { // ... public function initConfiguration() { $configCache = $this->getConfigCache(); // ... // required core classes for the framework if (!$this->isDebug() && !sfConfig::get('sf_test') && !self::$coreLoaded) { $configCache->import('config/core_compile.yml', false); // 原因はこいつ! } // autoloader(s) $this->dispatcher->connect('autoload.filter_config', array($this, 'filterAutoloadConfig')); sfAutoload::getInstance()->register(); if ($this->isDebug()) { sfAutoloadAgain::getInstance()->register(); } // ... } }
sfAutoload::register()を実行するまえに、core_compile.ymlを読み込もうとしてくれています。このとき、sfConfigCacheにはコンフィグハンドラーは一切設定されていないため、config_handlers.ymlを読み込もうとします。
<?php class sfConfigCache { protected function callHandler($handler, $configs, $cache) { if (count($this->handlers) == 0) { // we need to load the handlers first $this->loadConfigHandlers(); } // ... } protected function loadConfigHandlers() { // manually create our config_handlers.yml handler $this->handlers['config_handlers.yml'] = new sfRootConfigHandler(); // application configuration handlers require $this->checkConfig('config/config_handlers.yml'); // ... }
基本はsfConfigCache::checkConfig()にコンフィグファイルの名前を指定すると、内部でcallHandler()が呼ばれます。最初にcallHandler()が呼び出された際は$this->handlesが空なのでloadConfigHandlers()が呼ばれて、その内部でconfig_handlers.ymlを読み込むということを行っています。
たとえばconfig_handlers.ymlのroutingにMyRoutingConfigHandlerクラスを設定していたとしましょう。config_handlers.ymlはsfRootConfigHandlerクラスでYAMLからPHPコードに変換されます。
# apps/frontend/config/config_handlers.yml config/routing.yml: class: MyRoutingConfigHandler
<?php // cache/frontend/config/config_config_handlers.yml.php // ... $this->handlers['config/routing.yml'] = new MyRoutingConfigHandler();
で、ここでMyRoutingConfigHandlerがnewされているわけですが、オートローダーが登録されていなければもちろんクラスが見つからずエラーになってしまいます。prodとかだと登録される前なので実際にエラーがでちゃった、というわけです。。。
これ解決しようと思ったらrequireしろって話です。frontendConfigurationの中にrequire_onceを書いて対応しました。実際はMyPluginの中に入れていたので、次のようにしました。
<?php class frontendConfiguration extends sfApplicationConfiguration { public function configure() { require_once $this->pluginConfigurations['MyPlugin']->getRootDir() . '/lib/config/MyRoutingConfigHandler.class.php'; } }
追記
id:innx_hidenoriさんから一瞬で返答が返ってきました。
config_handlers.ymlの各エントリにfileってオプションを指定すると、指定したファイルを直接require_onceしてくれた気がしますが、そういう話とは別です?
Hidenori Goto on Twitter: "@fivestr config_handlers.ymlの各エントリにfileってオプションを指定すると、指定したファイルを直接require_onceしてくれた気がしますが、そういう話とは別です? #symfony_ja"
# apps/frontend/config/config_handlers.yml config/routing.yml: class: MyRoutingConfigHandler file: <?php echo ProjectConfiguration::getActive()->getPluginConfiguration('MyPlugin')->getRootDir() ?>/lib/config/MyRoutingConfigHandler.class.php
知らなかったよ・・・。
さらに追記
はてブのコメントでid:brtRiverさんより。
symfonyでrequireしないと駄目なパターン. see also: http://d.hatena.ne.jp/vector_xenon/20090608/1244484490
はてなブックマーク - config_handlers.ymlを書き換えるときの注意点 - ゆっくり*ゆっくり
んで、id:vector_xenonさんの記事をみて気付いたのですが、そういえば%SF_XXX%とかって書いておくと置換してくれましたね・・・。
# apps/frontend/config/config_handlers.yml config/routing.yml: class: MyRoutingConfigHandler file: %SF_PLUGINS_DIR%/MyPlugin/lib/config/MyRoutingConfigHandler.class.php
すっきり!