28 Nov
Handling Multiple Environments In Your PHP Application
In anticipation of my talk at PHP Quebec 2009 I've been going over my slides and thinking about what I'm going to update for it. One little nugget I'd thought I'd share is one way of handling having multiple environments your code must run in.
For a work application, I have *three* environments that the code must run in: 'dev' (which is my laptop), 'staging' (which is where I look for the infamous "bug does not exist in production" type of errors), and 'production'. For all these environments, there are different web services that they need to speak to so I tried to think of an easy way to handle this. I have a configuration file that is used, so it seemed to me that this would be a logical place to do the *check* for what environment we are in. But *how* to define where we are? That turned out to be easy.
If you are using Apache, you have the ability to define server variables that can be read in using $_SERVER. I'm sure you can do the same thing with other web servers, but this example is Apache-specific. So, I figured the best way to handle this problem was to define a server variable and have a unique value for each environment. Then, I added in code that checks the contents of that server variable and reacted accordingly. Here's an example:
-
<?php
-
// In Apache, add SetEnv APP_ENV "<unique environment value>" somewhere in your http.conf file
-
// e.g. SetEnv APP_ENV 'dev'
-
-
switch ($_SERVER['APP_ENV']) {
-
case 'dev':
-
$config['webservice'] = 'http://admin@localhost:8008';
-
$config['putfeed'] = 'http://dev.domain.com:8080/put/sml';
-
break;
-
case 'production':
-
$config['webservice'] = 'http://admin|password@ws.domain.com:8080';
-
$config['putfeed'] = 'http://in.domain.com/put/sml';
-
break;
-
}
-
?>
(NOTE: Jonathan Snook asked me to add an example on how to configure SetEnv)
Then, in your Apache httpd.conf file you add the SetEnv directive. Here's a snippet of how I did it for a work project:
ServerName local.sportso.com DocumentRoot /Users/chartjes/Sites/sportso.com SetEnv SPORTSO_ENV "dev" Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny Allow from all RewriteEngine on RewriteCond %{QUERY_STRING} ^last-accessed=(.*)$ RewriteRule ^(.*)$ $1/last-accessed/%1? RewriteRule ^$ /index.php [L] RewriteRule ^/$ /index.php [L] RewriteCond $1 !^(index\.php|images|styles|js|robots\.txt) RewriteRule ^(.*)$ /index.php/$1 [L] LogLevel warn ErrorLog /opt/local/apache2/logs/sportso.com-error.log CustomLog /opt/local/apache2/logs/sportso.com-access.log combined
Hope that helps out any other developers struggling to think of an elegant way to determine what environment you are in. As opposed to the lazy method of checking a production version of the config file into your version control system and then simply not checking back in the hacked-up dev version of it. ![]()

Posted by Brian on 28.11.08 at 11:42 am
If your dev and production servers have names (that presumably won't change frequently) you can alternatively write a switch on $_SERVER['SERVER_NAME'], as well. We're on IIS where I work and it's more convenient to work with that than to define other variables.
Posted by Chris Hartjes on 28.11.08 at 11:42 am
@Brian
SERVER_NAME is okay but I don't know if (a) it can be fully trusted and (b) what if your app is deployed across multiple servers? Maybe an extreme case, but not one you can totally discount.
Posted by Neil Crookes on 28.11.08 at 11:42 am
You inspired me to post about a similar solution I use in my Cake Apps. I like your APP_ENV apache solution though, might steal that one for my own. Cheers Chris, keep up the good posts.
Posted by Neil Crookes on 28.11.08 at 11:42 am
Runtime Config in CakePHP apps... the link might help, d'oh!
Posted by Jonathan Snook on 28.11.08 at 11:42 am
Ooh, what a great idea! I like it. You should've linked up or posted how to set the server variable in Apache for the lazy people. (No, not me, I can find it...I'm not lazy. meh, I'll look into it later...)
Posted by Nasko on 28.11.08 at 11:42 am
What I do is branch the environment specific folders and files in the SVN and then just switch them in my working copy. This way I just don't bother to check what the current environment is in the code.
I'd usually use the trunk for the production environment. This way a normal deployment script including an export from the trunk would operate on the production files only.
Then I'd create folders as branches in the SVN for each additional environment, e.g. dev, staging, etc. Normally I'd name the environment folders after the hostnames the would be deployed on. This gives the additional benefit of having specific environment folders for different members of the development team, should they need to have specific settings, etc.
The resulting SVN structure would look something around this:
SVN
|_trunk
|_tags
|_branches
|_environments
|_development
| |_app
| |_config
| | |_database.php
| | |_core.php
| |_webroot
| |_files
|_staging
| |_app
| |_config
| | |_database.php
| | |_core.php
| |_webroot
| |_files
|_john.dowe
|_app
|_config
| |_database.php
| |_core.php
|_webroot
|_files
As soon as a new environment is needed, or an additional developer is added to the team, we create a corresponding folder under branches and add under it it just the environment-specific folders - like database settings and other configuration, plus a folder for any user-uploaded content. Then the new developer would checkout the trunk and in their working copy would switch just the env.-specific files - to the ones under their respective folder under branches/environments.
This setup allows for both - having everything (environment specific configuration files, plus user-generated content, like uploaded files, etc.) under version control, and not overwriting each other's settings.
Then if someone needs to modify the configuration (e.g. add a new environment-specific setting, may be an SMTP server name), they can propagate the modification through to all other environment files, so all other members of the team would get it when they update their working copies.
For projects in which I'm the only developer, I'd just have folders for each environment. I normally don't care if my staging version is a working copy, so on the staging server I just checkout the trunk and switch only the staging-specific environment files. Then the normal working cycle wold be to work on development box, from time to time update the working copy on the staging environment and let the client preview the functionality, then finally export the trunk (which represents the production specific code) and upload it via SCP or http://FTP.
This might look as a slightly complicated setup to maintain, but once you're used to it it proves quite useful. At least for me.
Posted by Chris Hartjes on 28.11.08 at 11:42 am
@Nasko
Interesting way of doing it, and probably better suited to a multi-developer environment than my method is.
Posted by Loïc Hoguin on 28.11.08 at 11:42 am
I use this: http://wee.extend.ws/wiki/ConfigurationFiles
There's only a few targets available but it could get improved easily by adding a custom target function or other useful targets, like yours.
So far it's been working perfectly using the host target, I just have to svn update on any of the dev/prod/test/.. servers/laptops/... and it's working. You can even use different configuration variables for different development environment if you wish. No "multiple servers in a prod environment" tested yet, but since the prod environment settings are the default ones in my files, that wouldn't be a problem.
Posted by Chris Hartjes’ Blog: Handling Multiple Environments In Your PHP Application : WebNetiques on 28.11.08 at 11:42 am
[...] Hartjes has posted his method for creating a development setup that lets you use multiple environments with your [...]
Posted by Robin on 28.11.08 at 11:42 am
Same solution, different aproach:
create a file `system.id` in every checkout which contains the environment set and just do a switch / case statement on the contents of that file... (and set the default of that switch to the dev-version)
That way the cli-version of the application will also have that environment variable set, it works in shared hosting setup's and it's real easy to implement
[code]
if (is_readable(dirname(__FILE__).'/system.id')) define('SYSTEM_ID', trim(file_get_contents(dirname(__FILE__).'/system.id')));
else define('SYSTEM_ID', SYSTEM_ID_DEV);
error_reporting(E_ALL);
switch ( SYSTEM_ID ) {
case SYSTEM_ID_LIVE:
error_reporting(E_NONE);
break;
etc etc
Posted by Chris Hartjes’ Blog: Handling Multiple Environments In Your PHP Application : Dragonfly Networks on 28.11.08 at 11:42 am
[...] Hartjes has posted his method for creating a development setup that lets you use multiple environments with your [...]
Posted by Jani Hartikainen on 28.11.08 at 11:42 am
This is a good tip, I've used this approach several times with some Python apps and it works pretty nicely. Apache is full of useful small things
Posted by derek on 28.11.08 at 11:42 am
I made a system that uses phing/ant to build a static config file for whatever environment/developer combo you want.
Each developer, and each system gets its own config file, which are kept under version control.
When you run the build script, you must tell it which system to build for, and you *can* tell it (optionally) which developer's config to use. The developer's config values override those of the environment, so that they can have their own paths etc.
Makes switching 'environments' quick & painless.
Posted by Revue de presse | Simple Entrepreneur on 28.11.08 at 11:42 am
[...] Handling multiple environments in your PHP application Une approche originale pour détecter l’environnement (développement, staging, production,…) sur lequel une application est déployée et ainsi fournir des réglages spécifiques à cette plateforme. Qu’en pensez-vous ? [...]
Posted by CakePHP Digest Volume #3 :: PseudoCoder.com on 28.11.08 at 11:42 am
[...] There were also a couple interesting posts about handling environment specific settings in Cake apps. Personally I’m a fan of having a “boostrap.local.php” file that isn’t stored in SVN and is included in the normal bootsrap file. There’s no sex appeal to this method though, so if you don’t want to be standing alone in the corner while everyone else dances the night away, I suggest you read the posts by Neil Crookes and Chris Hartjes. [...]