Home > Tutorials > Using local configuration files in Zend Framework

Using local configuration files in Zend Framework

Posted by dstockto on August 10, 2011

I've developed numerous applications using Zend Framework so far during my career. In my experience, for each of these application that is developed with a team, I've run into a common issue. This issue is multiple developers each needing a slightly different configuration from each other.

Inevitably this means that each developer needs to maintain their own changes to a common configuration file. This leads to problems where a developer either accidentally checks in their modified version of the configuration file and thus messes up their co-workers, or, if they are careful and need to make a legitimate change the config file, they've got the hassle of backing up all of their customizations, making the changes and then re-applying their own customizations. In either case, all the other developers then have to then deal with merging in any changes manually as well.

This tutorial shows you how to make some minor modifications to your Zend Framework MVC application to allow developers to supply their own local.ini which is not checked into the repository and can override any settings they want while not having to worry about any of the problems listed above.

If you've configured your Zend Framework MVC project in the standard way (ie, following the Getting Started tutorial or using Zend Tool -- zf create project foo), then in your /public directory, you'll have an index.php file that looks a lot like below:

  1. // Define application environment
  2. defined('APPLICATION_ENV')|| define('APPLICATION_ENV',
  3. (getenv('APPLICATION_ENV') ?
  4. getenv('APPLICATION_ENV') : 'production'))
  5.  
  6. // Ensure library/ is on include_path
  7. set_include_path(implode(PATH_SEPARATOR,
  8. array(realpath(APPLICATION_PATH .
  9. '/../library'),get_include_path(),)));
  10.  
  11. /** Zend_Application */
  12. require_once 'Zend/Application.php';
  13.  
  14. // Create application, bootstrap, and run
  15. $application = new Zend_Application(APPLICATION_ENV,
  16. APPLICATION_PATH . '/configs/application.ini');
  17. $application->bootstrap()->run();

As you can see, the application is configured during instantiation here:

  1. // Create application, bootstrap, and run
  2. $application = new Zend_Application(APPLICATION_ENV,
  3. APPLICATION_PATH . '/configs/application.ini');

In order to make the local configuration file work, we'll need to make some modifications. We'll be using Zend_Config_Ini, and the autoloader has not been loaded yet, we'll need to load it manually so add this code right after the other require_once line:

  1. require_once 'Zend/Application.php';
  2. require_once 'Zend/Config/Ini.php';

Next we need to load the application.ini into a Zend_Config that we can override. Add this after the require statement.

  1. $defaultConfig = new Zend_Config_Ini(
  2. APPLICATION_PATH . '/configs/application.ini',
  3. APPLICATION_ENV,
  4. array('allowModifications' => true)
  5. );

This bit of code will load the ini that we know will exist. The second parameter says to load the section for the environment your application is running in. The third parameter will allow us to merge another configuration into another.

Next we'll check that the local.ini exists and load it into another configuration object. Note that we're loading the entire config file and not just a section. The reason for this is that we may want to have overrides in the file for a particular environment and not others. If we tried to load just a particular section that doesn't exist, it will throw an exception which I'd rather not deal with.

If the section exists, the $supplementalConfig file will be updated to just contain the configuration from that section. Then the default application.ini config is merged with the local.ini config. Any of the settings in the appropriate section in local.ini will override and settings that already exist.

  1. if (file_exists(APPLICATION_PATH . '/configs/local.ini')) {
  2. $supplementalConfig = new Zend_Config_Ini(
  3. APPLICATION_PATH . '/configs/local.ini');
  4. $env = APPLICATION_ENV;
  5. if (isset($supplementalConfig->$env)) {
  6. $supplementalConfig = $supplementalConfig->$env;
  7. $defaultConfig->merge($supplementalConfig);
  8. }
  9. }

The only thing left to do to make this all work is change how the application is initialized.

  1. // Create application, bootstrap, and run
  2. $application = new Zend_Application(
  3. APPLICATION_ENV,
  4. $defaultConfig
  5. );

The only thing left is to create a local.ini file in your /application/configs directory. You can create as many sections in your local.ini as you want. Keep in mind that the local.ini must be able to stand on its own. This means if you have a [development : production] section in your application.ini and want to override development settings, you just need a [development] section in your local.ini. However, if you do also have a [production] section in your local.ini you can inherit values in your local.ini and have a [development : production] section.

When you're all done, your file should look like this:

  1. // Define application environment
  2. defined('APPLICATION_ENV') ||
  3. define('APPLICATION_ENV',
  4. (getenv('APPLICATION_ENV') ?
  5. getenv('APPLICATION_ENV') : 'production'));
  6.  
  7. // Ensure library/ is on include_path
  8. set_include_path(implode(PATH_SEPARATOR,
  9. array(realpath(APPLICATION_PATH . '/../library'),
  10.  
  11. /** Zend_Application */
  12. require_once 'Zend/Application.php';
  13. require_once 'Zend/Config/Ini.php';
  14. $defaultConfig = new Zend_Config_Ini(
  15. APPLICATION_PATH . '/configs/application.ini',
  16. APPLICATION_ENV,
  17. array('allowModifications' => true)
  18. );
  19. if (file_exists(APPLICATION_PATH . '/configs/local.ini')) {
  20. $supplementalConfig = new Zend_Config_Ini(
  21. APPLICATION_PATH . '/configs/local.ini');
  22.  
  23. $env = APPLICATION_ENV;
  24. if (isset($supplementalConfig->$env)) {
  25. $supplementalConfig = $supplementalConfig->$env;
  26. $defaultConfig->merge($supplementalConfig);
  27. }
  28. }
  29.  
  30. // Create application, bootstrap, and run
  31. $application = new Zend_Application(
  32. APPLICATION_ENV,
  33. $defaultConfig
  34. );
  35.  
  36. $application->bootstrap()->run();

I hope this tutorial is helpful to you. If you have any questions or comments, please feel free to post them.

Thanks,
David

Comments:

Posted by Philip Sharp on
David -- I recommend adding local.ini to the svn:ingore (or similar setting for git, etc.) so that nobody accidentally commits their override file. Some IDEs are a little too helpful in committing new files.
Posted by Oscar on
A good practice would also be to check in a minimal and documented local.ini-sample file to your version control so that programmers can discover and use it.
Posted by Rob on
Perhaps I'm nitpicking, but wouldn't you rather create an "application.ini.dist" file, and ignore the "application.ini" file? That way you don't need to hit the file system every request for this additional file. At minimum, I would add 'production' !== APPLICATION_ENV in the conditional. Just some thoughts.
Posted by dstockto on
@Philip: Absolutely correct. That's what I've done when building this solution. I probably should have made a note or suggestion of that in the tutorial, but now you have, so thank you.

@Oscar: You are also correct and that is what I did as well (with the name of local.ini-dist with some comments about how ini inheritance works and how the ini should be built. The dist or sample file is checked in. Combine that with Philip's comment and it's now a system that someone can checkout, start work on and if they need to, have an example file showing them how to make non-intrusive local config changes on their local development environment.
Posted by dstockto on
@Rob: I may be missing something you're suggesting or perhaps not understanding. I'm assuming the comment about 'production' != APPLICATION_ENV would be to ensure that in production mode the code would not read the local.ini?

As far as the application not reading the application.ini on every request, this would mean, unless I am also misunderstanding, that you'd have to hard code the production config settings into the application. I'd rather leave that level of optimization for a cache system than deal with the headache of trying to find all the places where there were hard-coded production config settings that I'd need to change when the time came that something had to change. Please correct me if I'm not understanding you correctly.
Posted by Rocco on
Zend_Application can take an array of config files.
So an easier way is

// Create array of config file(s)
$localConfig = APPLICATION_PATH . '/configs/local.ini';
$globalConfig = array(APPLICATION_PATH . '/configs/application.ini');
if (file_exists($localConfig)) {
$globalConfig[] = $localConfig;
}

// Create application, bootstrap, and run
$application = new Zend_Application(APPLICATION_ENV, array(
'config' => $globalConfig
));
Posted by Wil Moore III on
Interesting approach. Mine is similar:

Important bits start at line #88:
https://gist.github.com/937433#file_bootstrap.php

At the end of the day, the .ini.dist file is the default, but the .ini file is used if found. The .ini file is ignored and local settings are added in the .ini file. For production, the .ini file is generated by the build script.
Posted by dstockto on
@Rocco and @Wil: I was not aware that it could take an array as an argument. I will play with it and learn more. It does appear that the order of the ini files in the array is the opposite between your two examples. I would assume the first file in the array would be over-ridden by the later files.

Thank you both for the insight. I'll look into it and update the tutorial.
Posted by David Stockton on
So a bit of an update/reply to @Rocco: I've done some additional research and testing on how the application can use an array of config files. Here's what I've found:

1) The order of the configs matters. The files defined later in the array can override the earlier defined config files. The order defined in @Rocco's example would work like the tutorial was written with local.ini overriding values in the application.ini.

The downside I see to doing the configuration without some of the error checking is that if the local.ini exists but doesn't have the section for the particular environment that is being run, the application will fail to start.

Anyway, I'd say it's a matter of preference which way someone wants to go. With the error checking the amount of code is more or less the same since the configs need to be loaded up (and checked) and then at that point pass in the config objects instead of just the file path/names. From the tutorial this would mainly be to not merge the configs and just use the array.

If there is an interest I'll update the tutorial with both examples, but right now I don't see any reason to.
Posted by dstockto on
I've uploaded source code for this tutorial to bitbucket.org. The link and information are in the sidebar. I created a branch for the array configuration. It appears that it cannot load an array of configuration objects, but will handle an array of file paths/names.
Posted by dstockto on
Super excited to see more of this kind of stuff online.
Posted by Zaiya on
Thanky Thanky for all this good inforamtoin!
Posted by andy on
Is there supposed to be code in this article? Seems to be missing.
Posted by dstockto on
The source code is linked in the right side bar. It's available from bitbucket.org.
Leave a Reply



(Your email will not be publicly displayed.)

Please type the letters and numbers shown in the image.Captcha CodeClick the image to see another captcha.