2. Environmental Protection Codes
CodeIgniter supports the idea of multiple Environments in which your application may run. This allows you to configure your application to behave differently in different server environments. It’s not as scary as it sounds, and it’s extremely useful.
The environment itself is simply a way of letting the application know whether it’s running on a developer’s personal server at the office, or the production server that’s running the live site. Depending on your workflow, you might have one or more staging or QA servers that function like the real environment, but use different databases, etc.
First, we will look at how to determine which environment is running. Then, we’ll take a look at what changes are made to the system. Finally, we will run through a few different ideas for using the different environments in your workflow.
Determining the Environment
The environment is determined on each page run at the top of the main index.php file.
39 /*
40 *---------------------------------------------------------------
41 * APPLICATION ENVIRONMENT
42 *---------------------------------------------------------------
43 *
44 * You can load different configurations depending on your
45 * current environment. Setting the environment also influences
46 * things like logging and error reporting.
47 *
48 * This can be set to anything, but default usage is:
49 *
50 * development
51 * testing
52 * production
53 *
54 * NOTE: If you change these, also change the error_reporting() code below
55 */
56 define('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'develop\
57 ment');
58
59 /*
60 *---------------------------------------------------------------
61 * ERROR REPORTING
62 *---------------------------------------------------------------
63 *
64 * Different environments will require different levels of error reporting.
65 * By default development will show errors but testing and live will hide them.
66 */
67 switch (ENVIRONMENT)
68 {
69 case 'development':
70 error_reporting(-1);
71 ini_set('display_errors', 1);
72 break;
73
74 case 'testing':
75 case 'production':
76 ini_set('display_errors', 0);
77 if (version_compare(PHP_VERSION, '5.3', '\>='))
78 {
79 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTIC\
80 E & ~E_USER_DEPRECATED);
81 }
82 else
83 {
84 error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
85 }
86 break;
87
88 default:
89 header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
90 echo 'The application environment is not set correctly.';
91 exit(1); // EXIT_ERROR
92 }
The first section, labeled APPLICATION ENVIRONMENT is where the actual environment is set. Unless you tell it differently, it will default to an environment named development. There are a few different ways that you can tell CodeIgniter which environment it is currently running in.
No matter how the environment is detected, the end result is always a newly defined constant named ENVIRONMENT that can be used anywhere in your application to modify how the app works at run-time. We’ll cover a few different use cases for this below.
Environment Setup under Apache
If you look at line 56, you’ll see that it’s checking for an environment variable called CI_ENV in the $_SERVER array. You can set that pretty easily in your site’s main .htaccess file (or other server configuration files) with the SetEnv command. You would add this line anywhere outside of any <IfModule> or similar blocks.
SetEnv CI_ENV production
This would set the environment to production.
Environment Setup under nginx
Under nginx, you must pass the environment variable through the fastcgi_params in order for it to show up under the $_SERVER variable. This allows it to work on the virtual-host level, instead of using env to set it for the entire server, though that would work fine on a dedicated server. You would then modify your server config to something like:
server {
server_name localhost;
include conf/defaults.conf;
root /var/www;
location ~* "\.php$" {
fastcgi_param CI_ENV "production";
include conf/fastcgi-php.conf;
}
}
Manual Setup
A third option is to manually define the environment by changing line 56 for whatever server you are running on.
define('ENVIRONMENT', 'production');
The biggest drawback with this method, though, is that you must manually change the variable here whenever a new set of changes is pushed to the server. This is far from ideal, and is only recommended if you have some form of automated deployment script setup that can modify the file for you on the fly as it is deployed to each server.
Dynamic Manual Setup
The most flexible way to handle this is to have the script check the host it’s running on and compare it to a set of known domains to determine the correct environment. You might also provide a fallback to look at the domain name for common patterns that you and your team use to name your virtual hosts on your development machines.
39 $domains = array(
40 'test.myapp.com' => 'staging',
41 'myapp.com' => 'production'
42 );
43
44 // Have we defined a server for this host?
45 if ( ! empty($domains[$_SERVER['HTTP_HOST']]))
46 {
47 define('ENVIRONMENT', $domains[$_SERVER['HTTP_HOST']]);
48 }
49 // Or is it a development machine, like myapp.dev?
50 else if (strpos($_SERVER['HTTP_HOST'], '.dev') !== FALSE)
51 {
52 define('ENVIRONMENT', 'development');
53 }
54 // Else - be safe...
55 else
56 {
57 define('ENVIRONMENT', 'production');
58 }
In this version, we create an array of allowed hosts and their corresponding environments, then we check to see if the current host matches one of these. If the current host isn’t found, we check the hostname to see if it ends in .dev, like myapp.dev. This matches my local naming scheme, but you could easily modify this to match myapp.local, dev.myapp.com, or anything else your team might already use. Finally, if no match is found, we assume it’s production for the strictest default settings.
This is my preferred method for determining the environment, simply for its flexibility and the ability to “set it and forget it”. You don’t have to worry about maintaining multiple versions of the .htaccess files to specify production or staging servers. Once you setup the rules, you save the index file to your version control system, and things simply work as expected whenever you push code to any of the servers.
Environment Configuration
Once you have decided how to detect and set the current environment, you need to setup your application to really take advantage of this. Out of the box, CodeIgniter sets the error reporting to sane defaults based on the environment (see lines 66-90). These make great default settings, but you should feel free to modify them to meet your company’s (and application’s) needs.
The most common use for server environments is going to be setting up configuration files based on the environment. Common things you would want to vary based on environment might be:
- Database settings, so you access the right database(s) for each environment.
- Email library configuration, so you can use different email addresses/servers based on domain name, or even simply use
mailin development but setup anSMTPconnection everywhere else. - Third-party API settings, so you’re not mixing test API calls into your live data.
- Different cache settings in development, where you want to always see the changes instantly, or in production where long-term caching is a benefit.
- Prefix the page title with an abbreviation for the environment so you can tell with a glance at the browser tab which environment you’re looking at.
- And many more uses depending on your exact application.
A Few Uses
Configuration Differences
The simplest use-case for environments is to change configuration settings based on the environment, and we just went over a few examples of this. Here’s how you do it, though.
You can set default configuration values in the standard CodeIgniter configuration files. You can opt to use the settings for your production site, or to leave the default settings and keep things in their separate environments for clearer organization. The latter method is the method I generally like to use, because there’s less chance for confusion. Everything is in its place and clear for you, your team, and any future developers that might work on the project. It also keeps accidents from happening when production settings are accidentally used in development environments.
For each environment which will have its own settings, create a directory named after that environment within the config directory. For any settings you need to change, you would copy or create a new file with the same name as the standard CodeIgniter configuration file, but under the directory named for the environment. This file is read in after the main configuration file, so you really only need to copy the changed values into the new file.
To setup the database settings for our development environment, we first create a new directory, /application/config/development. This directory’s name matches the environment. Then we would copy /application/config/database.php to the new directory (config/development/database.php) and modify the new file’s settings for our local, developer-specific database.
Auto-Migrations
One thing I will commonly do is to setup my system with the capability to automatically perform database migrations when the page is visited. However, I only ever want this to happen in the development environment. Sometimes on the staging server, depending on the client I’m working with at the time, but never on the production server. We like to maintain total control over the process on the production server, and each client has slightly different needs when pushing to that environment. Plus, performing the database migrations automatically may have some performance implications, so you may want to have more control over when this occurs.
Setting this up is fairly easy, and something we will go over in detail in Chapter 6.
Development Tool Routes
I have had some sites where we had some tools that we needed available to the developers, but would be dangerous to make available in production. These might be tools that:
- Allow us to easily do some maintenance or browsing of the database.
- Allow us to “fix” some data that occasionally becomes corrupt, allowing the site to continue until we find the true fix.
- Allow us to push the current database data to one of the other servers.
- Allow us to browse raw data feeds from external sources to detect changes in schema, or help while creating data imports.
In times like this, I would check the environment in /application/config/routes.php and either show or hide the routes as needed.
if (ENVIRONMENT === 'development')
{
$route['devtools/(:any)'] = 'developer/tools/$1';
}
else
{
// Don't allow access to these routes at all.
$route['devtools/(:any)'] = '';
}
Asset Compilation
While I typically use a tool like CodeKit for this now, it’s not always feasible. At times, the team you’re working with might use something else. You can add a method to the constructor of MY_Controller, or even use a pre-controller hook, to fire off commands that will compile and compress any CSS stylesheets or Javascript files.
Debugging Tools
If you use tools like PHP Error, Whoops!, or Kint, you will probably want those only in your development environment. It doesn’t make sense to even load them up in the other environments as it’s an unnecessary waste of resources. You can modify your autoload file to check for the environment, too.
1 /**
2 * Load PHPError
3 */
4 if (ENVIRONMENT === 'development' && ! is_cli() && config_item('use_php_error'))
5 {
6 require(__DIR__ . '/../php_error.php');
7 \php_error\reportErrors(array(
8 'application_folders' => 'application',
9 'ignore_folders' => 'system',
10 'enable_saving' => false
11 ));
12 }
Conclusion
Environments are a powerful feature that every developer should be using to make their lives a little bit easier. My hope is that this chapter has not only shown you how to use them, but sparked your imagination with different ways to use them.
If you have other ideas of how to use environments, I’d love to hear them!