Zend Framework Hidden Gems: Zend_Config

History of Configuration

Managing configuration is a pretty simple thing to do in PHP, in fact, I’d bet that at some point in their programming career every php programmer has written code that looks something like this:


<?php
$db_host = 'localhost';
$db_name = "joe_db";
$db_user = 'joe';
$db_pass = 'secret_password';
...

?>

This works pretty well for 10 – 100 line scripts, but as soon as you get a bit bigger you realize that you’re going to have trouble accessing your configuration variables, so you decide to refactor your script and do something fancy like this:


<?php
$GLOBALS['config']['db_name'] = 'localhost';
...
?>

That works pretty well, now where ever you are in your script you can access your configuration. You discover PHP5 and start using classes to organize your code. You refactor your code and rid yourself of your include() hell by using PHP5’s magic autoload methods only to find that all your classes rely on the file that includes your configuration data. You now decide that you need to move your configuration data into a class, so you change your code to work like this:


<?php

class Config {
    private $vars = array();

    function __construct(){
       $this->vars['db_host'] = 'localhost'
    }

    function __get($var){
        return $this->vars[$var];
    }

    function __set( $var, $data ){
        $this->vars[$var] = $data;
    }
}

?>

Now your class is flexible, it will fit into your class higharchy, you will be able to fetch arbitrary data from it, you can even update the data in real-time. However, you soon find that you want to separate your configuration data from the business logic which manages your configuration data.

There are many reasons to manage your configuration data indepenantly from your business logic. One common reason is that you don’t want to enter your configuration data into the public versioning system that you use. You may also want to grant developers access to change the business logic on a configuration system without giving them access to change the actual configuration data.

We could continue on with this example, we could subclass this config class, and read in an XML file or INI file and then populate the config class with the data, but then we discover Zend_Config and realize that there is no point in reinventing the wheel.

Using Zend_Config

Zend_Config approaches the concept of managing configuration data with two focuses; Simplicity, and a Driver-based backend. Here is a simple example of managing configuration through Zend_Config.


<?php
require_once 'Zend/Config/Ini.php';
$config = new Zend_Config_Ini('./config.ini', 'database');

echo "
";
echo $config->username . '
'; echo $config->password . '
'; echo $config->port; echo "

";
?>

As in our last article, we are not using the Zend framework class loader. Since we are only using this one class we are reducing the number of files that need to be included. I’m doing it this way so that we can focus on the reusable components of the Zend Framework, not the Zend Framework as a framework.

If you were to use Zend_Config in your own project you could tweak your __autoload() function to automatically load the Zend Framework functions. This is possible because Zend Framework maintains a standard class to filename mapping, so you can easily determine the name of the file in which a class resides simply by converting the _ to your directory separator as seen in the following __autoload() example.


<?php

function __autoload( $foo ){
    if ( substr( $foo, 0, 4) == 'Zend' ){
        require_once strtr($foo, '_', DIRECTORY_SEPARATOR) . ".php";
    }
}

?>

Now we are relying on PHP5 built in functionality to autoload our classes, so we don’t have to worry about those pesky includes.

Back to the Zend_Config example.

To make this example work correctly, we’re going to need a ini file to parse. As explained above Zend_Config can work with any type of storage backend for which a driver exists, ini files are easy to create, so we will be using this for our example.


[database]
username = foo
password = bar
hostname = localhost
port = 555

[cache]
life = 60
cache.backend = Core

So we have our ini file, and when running the above example we see that Zend_Config essentially encapsulates our data storage mechanism into an object. So we can access the configuration variables as if they were simply properties of the $config object.

Now, you will notice that the second parameter of the constructor takes the section of the configuration file to load, if you were to add an array instead of a string you are able to load multiple configuration sections.

Zend_Cache has one more bit of syntactical sugar for our consumption, which I will show in the next example. You will notice in the configuration file example I used the key “cache.backend” instead of simply “backend”. This example will show you why:


<?php
$config = new Zend_Config_Ini('./config.ini', array('database', 'cache'));

echo $config->life;
echo $config->cache->backend;
?>

As you can see Zend_Config translates the ini syntax to the class->properties syntax, enabling us to organize our configuration data into easily manageable blocks.

Inheritance

So now we have covered everything involved with managing our configuration files. We have our site on our staging server and plan to move it to production. Traditionally you would have 2 configuration files, one named something like ‘staging.ini’ and the other one named ‘production.ini’ and all that needs to be done when you move the system onto your production server is to change the name of the configuration file that is being used. This has the downside of having to maintain similar data in both configuration files (for example database type, port number which will probably not change between your servers) to solve this problem, Zend_Config introduces the concept of inheritance in the config files.

Take a look at the following ini file.


[database]
database.username = development_user
database.password = devpassword
database.name = development
database.hostname = localhost
database.port = 555

[staging : database]
database.name = production_database
database.hostname = live.server.com

[production : staging]
database.username = live_production_user
database.password = tricky_password

In this scenario we have three development platforms. Our localhost development system which contains it’s own database, which has a user with full read/write access to the development database. On launch day we branch our development tree and export it to our staging server. At this point we need to have access to the production database so that we can run tests, however we use an unprivilaged user so that if something does go wrong with our testing we can’t do any damage to the live database. If all the tests go as planned, we then move the code to our production server where it uses the database user with full access to the database.

Now instead of having three configuration files for our three scenarios, with the help of Zend_Config, we only need one. The developers of Zend_Config have chosen to extend the traditional ini file format to enable an easy way to specify the inheritance of a section. In this ini file, the staging section inherits from the “database” section, and the “production” section inherits from the “staging” section. This way only the data that needs to be changed in each scenario is changed, and there is no data overlap.

In Conclusion

In future articles we will see Zend_Config in use, and talk about various strategies that can be used when dealing with configuration data. One thing to keep in mind is that while you can dynamically update the data in the Zend_Config object that you’ve created, there is no way of saving this data back to the datasource.

Another weakness in Zend_Config is the lack of configuration container backends. This is in comparison to mature packages like Config from PEAR which includes in read/write container backends for arrays, apache config files, php defines etc. However, seeing that Zend Framework hasn’t even reached version 1.0 yet, I’m sure we’ll see a lot of progress made on this front.

Coming up next in the series is the first in our multi-part series of articles on Zend_DB and friends.