Pass to content
Configuring PerchCMS with PHP dotenv

Configuring PerchCMS with PHP dotenv

Perch CMS comes out of the box with a mechanism to manage different environment vconfiguratons such as database credentials, smtp configuration and the like. You can define different configuration files for each environment and then switch which of the configuration files Perch uses based on the current URL.  It absolutely works without question. I have one main issue with this approach, that it tends encourage committing sensitive credentials in to the code repository potentially exposing that information at large to anyone with read access to the repository.  For people who are new to using GIT and repositories, this can happen more often than you might think. 

Personally I feel that Perch should be taking a more secure approach by providing a mechanism by which it is encouraged by default not to commit your credentials to the repository.  I like to take the .env method as is used by Laravel and some other frameworks, where the credentials and environment specific configuration are stored in an .env file and this file is listed in the .gitignore file so it will be ignored on commit by default.  This will ensure that the credentials will not be commited unless the developer has explicity removed the .env entry from being ignored.  The framework provides a file .env.example which contains a dummy example of what variables should be in the real .env file, and this example file is commited to the repository.  This allows any new developer to be able to easily configure the the applicaiton by copying the .env.example file to a new .env file and updating the configuration to pertain to their develpment environment.  This method also works when deploying to test, staging and production servers, where the server holds the server specific configuration in its .env file which is copied over to the application during the deployment process thus updating the application with the current environment just before being made public.

Reconfiguring Perch CMS

Step 1: Create the .env file and ignore it.

Be sure to have a .gitignore file at the root of your project, this will tell GIT to not include certain files or folders in a commit essentially ignoring them from the repository which is exactly what we want to do for the .env file.  For our purposes here we're going to create .gitignore at the root of the project and add a single line:


.env

Then I create the .env.example again in the project root folder in which I create a key=value entry on each line for each of the Perch CMS configurations such as:


# NOTE:  Never commit this file to the code repository
#ENVIRONMENT
# Options: dev | stage | prod
ENV="dev"
# Ouput Perch Debug information. Should be false in production
PERCH_DEBUG=false
# DATABASE
PERCH_DB_SERVER="localhost"
PERCH_DB_PORT=3306
PERCH_DB_DATABASE="database_name"
PERCH_DB_USERNAME="database_user"
PERCH_DB_PASSWORD="database_pass"
#PERCH CMS CONFIG
PERCH_KEY="AAAAAA-BBBBBB-CCCCCC-DDDDDD-EEEEEE"
PERCH_SCHEDULE_SECRET="cron-secret"
# Sending Email Settings
PERCH_EMAIL_METHOD="smtp"
PERCH_EMAIL_HOST="mail.server.com"
PERCH_EMAIL_PORT=465
PERCH_EMAIL_SECURE="ssl"
PERCH_EMAIL_AUTH=true
PERCH_EMAIL_USERNAME="mailfrom@server.com"
PERCH_EMAIL_PASSWORD="AAA123"
PERCH_EMAIL_FROM="noreply@server.com"
PERCH_EMAIL_FROM_NAME="From Name"
# Googele Maps JS API key
PERCH_GMAPS_API_KEY=""

Note that I wrap most of the values in double quotes, but this is not actually required, you only really need to do this if you have spaces in the values.  I do this just to be consistent with my file, I try to match the variables to their expected type, eventhough they will all be loaded as strings. This is just my personal preference.

Step 2: Install PHP dotenv

PHP dotenv is an open source library used to consume .env files and load the environment configuration into the $_ENV and $_SERVER super globals this makes them accessible via the getenv() php function. There are a tonne of options including specifiying requried values, validation and nested variables.  I encourage you to go over the project's README.md file to learn more.

PHP Dotenv is installed via composer, so if you don't already have a composer.json file at the root of your project, go ahead and initialize it now.  You can learn more about Composer at the official website getcomposer.org.  The command to install PHP Dotenv is:


$ composer require vlucas/phpdotenv

This will add vlucas/phpdotenv to your composer.json file and download the library to your projects vendor directory. Composer will create the vendor direcory if it doesn't already exist.  

Note that you should update your .gitignore to include the vendor directory so it is not commited as well.  Your .gitignore file should read something like:


.env
/vendor

Step 3: Update the Perch CMS configuration file

  1. Now that the .env file is setup and PHP dotenv is installed, we need to update the Perch CMS main config.php file:
  2. Copy the configuration generated during the Perch CMS install from the environment configuration file config.production.php to the main config.php so all of the configuration for the site is found in the main config.php.  
  3. Remove the section in the configuration that switches the configuration file based on the host, we won't need that any more.
  4. Add a require command to the very first line of the config.php to require the Composer autoload.php. It is located in the vendor directory that Composer created on install. The line should look similar to the following, note that the number of relative paths back up the directory tree may vary depending on your site structure:

<?php
include(__DIR__ . '/../../../vendor/autoload.php');
  1. Now that the Composer autoloading is added, we can include the PHP Dotenv class by using a PHP use statement at the top of the file, and use the Dotenv class to load the .env file:

<?php
use Dotenv\Dotenv;
include(__DIR__ . '/../../../vendor/autoload.php');
$dotenv = Dotenv::create(__DIR__ . '/../../../');
$dotenv->load();
  1. The Dotenv::create function takes the path parameter that should lead back to the .env file in the root of your project, make sure the path points to your directory containing the .env file. It does not need to include the .env file in the path. For example:  By providing __DIR__ . '/../../../' as shown, will point three directories up from the current directory of the config.php file.
  2. Create an array of required Perch CMS constant names that match the variables stored in the .env file.  We will use this array to validate that they are indeed provided in the .env file using the Dotenv::required() function. In the same loop we will set the constant required by Perch CMS as well as remove the data from the $_ENV and $_SERVER super globals so they do not show up in the Perch CMS extended diagnostic information:

<?php
use Dotenv\Dotenv;
include(__DIR__ . '/../../../vendor/autoload.php');
$dotenv = Dotenv::create(__DIR__ . '/../../../');
$dotenv->load();
$requiredVars = [
    'PERCH_DB_USERNAME',
    'PERCH_DB_PASSWORD',
    'PERCH_DB_SERVER',
    'PERCH_DB_PORT',
    'PERCH_LICENSE_KEY',
    'PERCH_EMAIL_METHOD',
    'PERCH_EMAIL_HOST',
    'PERCH_EMAIL_SECURE',
    'PERCH_EMAIL_PORT',
    'PERCH_EMAIL_USERNAME',
    'PERCH_EMAIL_PASSWORD',
    'PERCH_EMAIL_FROM',
    'PERCH_EMAIL_FROM_NAME',
    'PERCH_LICENSE_KEY',
    'PERCH_SCHEDULE_SECRET',
];
$dotenv->required($requiredVars);
foreach ($requiredVars as $requiredVar) {
    //DEFINE THE CONSTANT FOR PERCH CMS
    define($requiredVar, getenv($requiredVar));
    //Remove sensitive information from $_ENV and $_SERVER
    if (isset($_ENV[$requiredVar])) {
        unset($_ENV[$requiredVar]);
    }
    if (isset($_SERVER[$requiredVar])) {
        unset($_SERVER[$requiredVar]);
    }
}
/**
* Omitting standard non-environment specific perch configuration such as : PERCH_LOGINPATH, PERCH_CORE, PERCH_RESPATH, etc.
**/
define('PERCH_DEBUG', filter_var(getenv('PERCH_DEBUG'), FILTER_VALIDATE_BOOLEAN);
if (PERCH_DEBUG) {
    error_reporting(E_ALL);
    ini_set('display_errors', PERCH_DEBUG);
}
switch (getenv('ENV')) {
    case 'dev' :
        define('PERCH_PRODUCTION_MODE', 'PERCH_DEVELOPMENT');
        break;
    case 'stage' :
        define('PERCH_PRODUCTION_MODE', 'PERCH_STAGING');
        break;
    default :
        define('PERCH_PRODUCTION_MODE', 'PERCH_PRODUCTION');
        break;
}

As you can see the config.php file uses the PHP getenv() function to pull in the same named environment variables that was loaded via the PHP Dotenv class to configure the Perch CMS application.  The second last bit deals with the PERCH_DEBUG variable which requires a bit more processing.  PERCH_DEBUG is not considered sensitive information or required data either, by default it will be false if not found in the .env file rendering a production environment.  We still use the getenv() function to retrieve the PERCH_DEBUG value, but pass it straight away to the filter_var() PHP function with the FILTER_VALIDATE_BOOLEAN flag which will return a boolean true or false depending on the value of the variable. The FILTER_VALIDATE_BOOLEAN flag accepts different inputs so : 1,0, true, false, Yes, No are all valid inputs that will map to their boolean equivalent.  We do this so that we can use the boolean value to set error reporting for the application as needed.

The final piece is setting the Perch production mode which is mapped directly to the ENV variable in the .env file with Production being the default.  You could optionally use the Dotenv::allowedValues() function to validate its value such as:


$dotEnv->required('ENV')
       ->allowedValues(['dev', 'stage', 'prod']);

But thats not really required as the default is Production which is a sane default.

Note that the variables are loaded as strings, if you need a strict type such as a boolean, integer or float, you'll want to cast the value such as we did for the PERCH_DEBUG variable.

Integrating into the deployment cycle

No matter how you deploy your application you have a host of options on how to add the environment .env file to the project.  This approach really lends itself well to an automated deployment where the deployment scripts can be adapted to copy a .env file from outside the deployment directory into its desired location within the project just before pushing the site live.  I handle this with the use of a composer.json script that is triggered by the composer lifecycle hooks during the compser install that is executed by the deployment script. 

Here is an example of the script that is used on the post-install-deploy composer hook which happens during a regular composer install:


"scripts" : {
        "post-install-deploy" : [
            "cp ../../.env .",
            "cp ../../.htaccess public"
        ]
    }

Here we can see that there are 2 commands that are executed during the composer install that copy the .env file stored two levels up the directory tree to the current directory  which happens to be the root of the project, one level up from the public folder.  The second command copies the .htaccess file to the public folder, this allows you to deploy to a staging environment that may have a separate .htaccess configuration such as for Basic Authentication to limit access to the staging site.  

Updating Perch CMS:

When updating Perch CMS, you may end up overwriting the configuration you just set in the config.php file.  When updating perch, first you should be doing this on an isolated instance, not on your production website and I would recommend creating a new feature branch of the GIT repository to do this and test the update.  Secondly, normally you really only need to replace the perch/core/ folder.  Since you shouldn't be playing around with the core files, this is the safest update route to take that will and will not break your reconfigured Perch instance.  Alternatively, you can merge the folders together and use a diff tool such as WinDiff, Meld, KDiff or my favorite: Kaleidoscope to validate any changes and revert unwanted modifications to your config.php file.

Conclusion:

Following the above procedure will modify the default Perch CMS configuration to use the PHP Dotenv method. Its my standard method of configuring a Perch CMS system as well as any other PHP application I create. Using this method has a number of advantages: It ensures that you do not commit environment specific credentials or configuration to the repository keeping the data more secure and less prone to leaks.  You can easily document the needed configuration for the application. Finally, the integration into a deployment pipline or procedure is extremely easy to setup and automate. 

Also if you're using Docker, you can configure the .env file as a volume on the application container which will be mounted in the correct directory to be used by the containerized application, keeping your environment configuration out of the Docker image.

Alternatives:

If you do not want to use an external library such as PHP Dotenv and you have access to the server configuration, you can always configure environment variables for the server there as well and continue to use the PHP getenv() function.  This can work really well for single developers and freelancers.  The main reason I choose the PHP Dotenv route over the server configuration is portablilty and documentation. Having the .env.example file makes it really easy to document and share what the needed configuration is and how a new developer needs to setup their development environment. This is great when a project changes hands or is inherited by another developer.

Limitation of Responsibility:

This blog post documents procedures that Cognetif executes during web development.  You should not be playing around and testing this procedure on your production website, instead you should be testing on a separate non-public instance.  If you break your website following these procedures Cognetif will not be held responsible for any loss of any kind, although we will gladly help you integrate this solution or correct any issues that my arise at our regular rate.  Note that Perch CMS exposes environment variables in the diagnostic information available in the settings pages of the CMS, if you do not want this, ensure you use the documented code above that removes the variables from the $_ENV and $_SERVER super globals.  If you use this method and decided to copy and paste your diagnostic information publically available on the internet, be sure to remove any sensitive information such as database and email credentials, Perch license keys etc.

Categories: Web Development PHP DevOps Tips GIT Perch CMS

Leave a comment