Writing powerful and easy config files with PHP-arrays
Friday, May 8, 2009
I was asked many times how I organize my config files, and my response was always the same, until some time ago when I switched began refactoring the codebase of my blog. I always used PHP config files in some way (as I got inspired to it by Matthew Weier O'Phinney). So first let's clearify the advantages of PHP-array config files:· they allow to organize one config into multiple files
· they can be cached by an opcode cache
· they support constants
· they allow to create easily readable config trees
· they support boolean and integer values
Looking at those advantages, you may ask now why not everbody is using them. Well the problem mostly is that you cannot create extend-sections (when working with Zend_Config for example). So in the past I always had to create separate config files for each development and production environment. When I started refactoring the codebase I thought about that problem and came to a very simple solution. First you have your base config file looking somehow like this:
This config file uses array_merge_recursive to tage a basic config array and overwrite or extend properties from an environment specific config file (in this case development.config.php and production.config.php). Additionally the routes for the router are outsourced into a separate file, since they are taking up a lot of space. Now let's take a look at the the development.config.php:<?phpreturn array_merge_recursive(array('resources' => array('frontController' => array('moduleDirectory' => APPLICATION_PATH . '/modules'),'router' => array('routes' => include dirname(__FILE__) . '/routes.config.php'),'db' => array('adapter' => 'pdo_mysql','params' => array('charset' => 'utf-8')))), include dirname(__FILE__) . '/' . APPLICATION_ENV . '.config.php');
As you can see, the development specific config file now enables all the error reporting, set the baseUrl for the front controller and the connection parameters for the database adapter. The production specific config file is doing similar things but disabling all error reporting. As you have seen in the base config file, two constants should already be set (APPLICATION_PATH and APPLICATION_ENV). To use the base config file within your Zend Framework application, you load it like this:<?phpreturn array('phpSettings' => array('display_startup_errors' => 1,'display_errors' => 1,'error_reporting' => (E_ALL | E_STRICT)),'resources' => array('frontController' => array('baseUrl' => '/some/dev/path','throwExceptions' => true),'db' => array('params' => array('host' => 'localhost','username' => 'foo','password' => 'bar','dbname' => 'baz'))));
If you want to see a full example of a working config-structure, you can take a look at my new codebase in my SVN repository.<?php// Loading the config manually$config = new Zend_Config(require APPLICATION_PATH . '/config/config.php');// Or telling Zend_Application to load the config$application = new Zend_Application(APPLICATION_ENV,APPLICATION_PATH . '/config/config.php');
Comments to this article
Leave a comment
Please note that your email address will not be shown, it is only used to fetch your avatar image from gravatar.com and for notifications.


Nice!
Better than my approach to it by a long way, I will be sure to use this notation when i refactor Zend_App into my projects!
Might be just the fact that i haven't been using much .ini files in the past, but your solution just seems natural for PHP development.
However, i think this might break some functionality when you want to use the configs for another enviroment, other than PHP. For interoperability i think .ini and .xml are better.
What do you think?
In fact this does not work when working with other programming languages as well. Tho in those cases the other programming languages usually just want you database connection settings, and not any information about PHP settings or application resources :)
mmm ... i don't see the benefit over using something else and caching it. your solution seems to be way more complicated compared to doing configuration with for example yml files.
i've thought about configuration files very much, too, when i designed my framework and i came to the conclusion, that yml works very well using spyc or syck.
@Haral: I named the benefits above, and IMHO the major one is that you are able to split the configuration into logical units.
I have per resource a file, all in a separate resource-dir (just like is done with Zym_App). My looks like this:
<?php
$resourceDir = dirname(__FILE__).'/resource/';
$config = array('resources'=>array());
// Scan for resource files
$resourceFiles = scandir($resourceDir);
unset($resourceFiles[0],$resourceFiles[1]);
foreach ($resourceFiles as $file) {
$config['resources'] = $config['resources'] + include $resourceDir . $file;
}
return array_merge_recursive($config,include dirname(__FILE__) . '/' . APPLICATION_ENV . '.php');
?>
-- Freeaqingme
I just use .ini files. Zend_Config can handle inheritance, so it's easy to create sections within a single .ini file.
Interesting, but how do you load front controller plugins eg
resources.frontController.plugins.auth = "Default_Plugin_Auth"
works, but this:
'resources' => array(
'frontController' => array(
'plugins' => array(
'auth' => 'Default_Plugin_Auth'
)
)
doesn't. Producing this error:
Declaration of Default_Plugin_Auth::preDispatch() should be compatible with that of Zend_Controller_Plugin_Abstract::preDispatch()
That's your problem, James. You have to define plugin's preDispatch with same arguments as those in Zend's abstract.
This approach has one major drawback, there is no code completion in the IDE (except jcx visual studio extension, correct if im wrong). What do you think about code generator (to create strongly typed config based on whatever format u like - less error-prone, can generate type hints etc.) ?
I use the same approach in my application, and it is really easy to manage the config files.
This is wonderful. Thanks Ben for such an easy approach :)
How about trying the Configuration eZ Component ?
http://ezcomponents.org/docs/tutorials/Configuration
Cheers :)
Jérôme, the array-config format in the eZ component is still theme as for Zend_Config and thus this article can also be applied there.
And I must disagree with an idea of creating config files in PHP arrays. Config files in my oppinion are the files, that keeps setings, that should be able to be moified by system administrator. Making them in PHP-array standard forces sysadmin to learn PHP - which in fact might not happen (he might be a Linux system admin - without programming knowledge).
Therefore I suppose the config files should rather be kept in more universal format - such as INI or .conf - which btw can be easily parsed by Zend_Config, changed to OO way and operated.
Voyteck, I wouldn't consider a Linux administrator without knowledge about the basics of a programming language actually an administrator. Also, to change e.g. the database username, password or soemthing similar in a PHP config file doesn't really require any knowledge about PHP (except for escaping, but that applies to any config file format, and is usally not required).
There is one problem with array_merge_recursive. Two "not-array" values with the same key will be merged together in an array.
< ?php
$a = array (
'config'=>array(
'db'=>array(
'dbname'=>'name')));
$b = array (
'config'=>array(
'db'=>array(
'dbname'=>'thisConfigUseAnotherName')));
var_dump (array_merge_recursive($a,$b));
/*
array(1) {
["config"]=>
array(1) {
["db"]=>
array(1) {
["dbname"]=>
array(2) {
[0]=>
string(4) "name"
[1]=>
string(24) "thisConfigUseAnotherName"
}
}
}
There are two more thing you can do with php/arrays that isn't possible in ini files as far as I know:
Calcuations:
$config['cookie_lifetime'] = 60*60*7; // makes it more readable
And if you're not doing it inside the nested structure, it's also possible to reuse already defined fields:
$config['tmp']['base'] = '/tmp';
$config['tmp']['logs'] = $config['tmp']['base'] . '/logs;
Sometimes i like to have such dependent fields.
@KingCrunch: I solve this problem, by not using the nested structure for arrays (a least in the additional files).
// file config.php
....
$config['config']['db']['dbname'] = 'production';
include dirname(__FILE__) . '/' . APPLICATION_ENV . '.config.php');
return $config;
// file dev.config.php
$config['config']['db']['dbname'] = 'test';
Dasprid, did you made any profiling to see if there is a significant speedup on loading the config file by using PHP arrays instead of let's say .ini files ?
I am interesting by any things which are speeding up the ZF apps...