This post requires some basic understanding of how ZF2 applications work. If you have never worked with ZF2 before you might want to head over to the Getting Started with Zend Framework 2 guide over at framework.zend.com before continuing.
For this post I will use a fictive application with 2 modules, called Application
and MyModule
respectively. The way we usually do this is to develop the modules by themselves (with the exception of the “main” module, if you can call it that), and then install the modules using Composer.
All modules have its default configuration in the config/module.config.php
file relative to the root of the module directory. When adding the module to our application we also add a config/autoload/<module name>.php
file for application specific module configuration. If we use our fictive application this leads to the following configuration files:
Main application configuration:
./config/application.config.php
Default configuration for the modules:
./module/Application/config/module.config.php
./vendor/vgno/mymodule/config/module.config.php
Application specific configuration for the modules:
./config/autoload/Application.php
./config/autoload/MyModule.php
Environment specific configuration for the modules:
./config/development/Application.php
./config/development/MyModule.php
./config/staging/Application.php
./config/staging/MyModule.php
./config/production/Application.php
./config/production/MyModule.php
Un-versioned local configuration file:
./config/local.php
Before we continue adding even more configuration files let’s have a look at the config/application.config.php
file which contains configuration related to how the rest of the files are loaded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [code language="php"]<?php return array( 'modules' => array( 'Application', 'MyModule', ), 'module_listener_options' => array( 'config_glob_paths' => array( __DIR__ . '/autoload/*.php', __DIR__ . '/current.php', __DIR__ . '/local.php', ), 'module_paths' => array( __DIR__ . '/../module', __DIR__ . '/../vendor', ), ), );[/code] |
The config_glob_paths
array defines the order of how the configuration files are loaded. First ZF2 will load the modules themselves (along with the default module configuration). Then it continues to load all .php
files in the config/autoload
directory which contains the application-specific module configuration. We also add a pattern for a file we have not yet mentioned: config/current.php
. Last we add a glob for an un-versioned configuration file that can be used when testing some experimental configuration without having to edit versioned files.
The config/current.php
file mentioned above is a symlink that is generated on deployment/installation. The link points to what we call an environment configuration loader. And what is this? Why, another configuration file of course! We have such loaders for all the different environments, and this is where we do the inheritance part as mentioned in the post title. Say we have three environments for our fictive application: development, staging and production. This maps to the following three files:
./config/development.php
./config/staging.php
./config/production.php
The development configuration does not really need to inherit any of the other environments, so the file can look like this:
1 2 3 4 5 6 7 8 | [code language="php"]<?php $config = array(); foreach (glob(__DIR__ . '/development/*.php') as $file) { $config = array_replace_recursive($config, require $file); } return $config;[/code] |
The production environment does not need to inherit any configuration either, so that file is pretty similar:
1 2 3 4 5 6 7 8 | [code language="php"]<?php $config = array(); foreach (glob(__DIR__ . '/production/*.php') as $file) { $config = array_replace_recursive($config, require $file); } return $config;[/code] |
Lastly, we have the staging environment, which could inherit the production configuration:
1 2 3 4 5 6 7 8 | [code language="php"]<?php $config = require __DIR__ . '/production.php'; foreach (glob(__DIR__ . '/staging/*.php') as $file) { $config = array_replace_recursive($config, require $file); } return $config;[/code] |
The only difference in the staging.php
file is that the initial configuration array is set to the production configuration, which it can override in the environment specific configuration files placed in the config/staging
directory. If we don’t want to override any configuration we can simply omit that directory.
As mentioned earlier the config/current.php
symlink is pretty crucial for this method to work. We use Capistrano for deployment, so we create the symlink during the deployment process. Since we don’t use Capistrano for deploying on the development server for all our developers we have an “install/prepare” task in ant/rake/phing/whatever that links the config/current.php
symlink to config/development.php
.
Since this method involves loading a rather huge amount of configuration files we cache the merged configuration. You can read more about how to achieve this over at Rob Allen’s blog: Caching your ZF2 merged configuration.
If anyone have any feedback to this method other than “Ermahgerd that’s a lot of config files, you suck!” feel free to leave a comment or two. Keep in mind that the fictive application I used only included two modules, so the benefit might not be too clear. The application we used this method in has more than 20 modules and 7 different environments, which would result in an ungodly amount of configuration files if we were to copy/paste configuration between environments instead of using inheritance.
And that’s it for now. Happy configuring!