A more flexible configuration

IHMO, the current default Yii application has several drawbacks:

  • I have to edit "index.php", especially to disable the debug mode.

I find it ugly and misleading to modify this central file for a simple change of the application settings.

  • I have to edit "protected/config/main.php" on each instance. For instance, to change the database settings.

These 2 files are necessary to the application, so they are usually versionned. That’s a pain, because I have to make local changes on each instance of the application. When I want to change one of them (e.g. add a new extension), there will be conflicts: git or hg will refuse to update (merge) a file that has local changes. I’d much prefer to have the local settings in a separate file.

I suggest a few changes to the application template.

First, there should be a way to know where the Yii framework is installed. For instance, this file could be useful to modules and extensions that want a direct access to Yii.


// yiipath.php

<?php

// change the following path if necessary

defined('YII_PATH') or define('YII_PATH', dirname(dirname(__FILE__)) . '/yii/framework');



The index file reads the configurations (global and local). Then it loads Yii. Then it merges the configs. Then run the Yii app. The order is important. For example, this way the configuration can set the debug mode.


// index.php

<?php

$mainConfig = require dirname(__FILE__) . '/protected/config/main.php';

$localConfig = require dirname(__FILE__) . '/protected/config/local.php';

if (empty($localConfig)) {

	die("Please configure this application.");

}

include dirname(__FILE__) . '/protected/config/debug.php';


require dirname(__FILE__) . '/yiipath.php';

require_once YII_PATH . '/yii.php';


$config = CMap::mergeArray($mainConfig, $localConfig);

unset($mainConfig, $localConfig);


Yii::createWebApplication($config)->run();



The first config file only contains non-local settings. It should be versionned.


// protected/config/main.php

<?php


/**

 * Do not modify this file unless you're a developper.

 * For configuring the application, use "local.php" in the same directory.

 */


// This is the main Web application configuration. Any writable

// CWebApplication properties can be configured here.

return array(

	'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..',

	'name' => 'XXXXXX',

	'language' => 'en',


	// preloading 'log' component

	'preload' => array('log'),


	// .....

);



The second file should not be in the central versionning system. It should be created, if necessary, on each instance, from a "local.dist.php" file.


// protected/config/local.php

<?php


/**

 * This file completes and overwrites the configuration set in "main.php".

 */


/**

 * If true, then the debug mode will be disabled (see "debug.php").

 * The configuration below also uses this setting

 */

defined('APP_PRODUCTION_MODE')

	or define('APP_PRODUCTION_MODE', false);


/**

 * For automated tests,

 * change the following URL based on your server configuration

 * Make sure the URL ends with a slash so that we can use relative URLs in test cases

 */

defined('TEST_BASE_URL')

	or define('TEST_BASE_URL', 'http://localhost/testdrive/index-test.php/');


$tmpconf = array(

	'components' => array(

		'db' => array(

			'connectionString' => 'mysql:host=localhost;dbname=XXX',

			'username' => '',

			'password' => '',

			'charset' => 'utf8',

			'enableProfiling' => !APP_PRODUCTION_MODE,

			'enableParamLogging' => !APP_PRODUCTION_MODE,

			'schemaCachingDuration' => (APP_PRODUCTION_MODE ? 3600 : 0),

		),

	),


	// application-level parameters that can be accessed

	// with the syntax Yii::app()->params['paramName']

	'params' => array(

	),

);


if (!APP_PRODUCTION_MODE) {

	// load Gii module, only for localhost access

	$tmpconf['modules']['gii'] = array(

		'class' => 'system.gii.GiiModule',

	);

}

return $tmpconf;



This last file translate the constant APP_PRODUCTION_MODE into the settings that Yii handles.


// protected/config/debug.php

<?php


/*

 * Modify this file if you want to change the debugging behaviour,

 * when APP_PRODUCTION_MODE is true or false.

 */


if (APP_PRODUCTION_MODE) {

	// in production mode, no debugging

	defined('YII_DEBUG') or define('YII_DEBUG', false);

	defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);

	ini_set('error_display', 0);

} else {

	defined('YII_DEBUG') or define('YII_DEBUG', true);

	defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);

}



I find this structure much easier to use than the default one. There is room for improvment (for instance, the APP_PRODUCTION_MODE constant could have several values instead of a boolean), but I tried to keep it simple for the user. I’d be delighted if Yii could make a step in this direction.

I am in full support of a mergeable configuration setup. It’d be nice to be able to separate system-specific configurations (caching mechanism, database credentials, etc.) from more static configurations, like system-critical components.

But since the configuration files currently are just PHP files, you could easily perform the merging yourself within them as things are. Before returning the array, just include the local file and merge the static and variable arrays together.

That’s how we actually develop with Yii. The problem with this setup out of the box is that it’s a bit complex for simple apps.