Pragmatism in the real world

Configuration in Slim Framework

Configuration in Slim Framework is nice and simple: the App‘s constructor takes a configuration array for the DI container;

$config = [];
$app = new Slim\App($config);

Setting up

The settings sub-array is used to hold the settings of your application:

$config = [
    'settings' => [
        'displayErrorDetails' => true,

        'logger' => [
            'name' => 'slim-app',
            'level' => Monolog\Logger::DEBUG,
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ]
];
$app = new Slim\App($config);

Slim comes with a number of settings that you can change. The most important is displayErrorDetails. This defaults to false, but if you set it to true, then it will display the details of any exceptions when rendering an error page. Ensure that this is set to false in production!

You can put any other settings you want to in the configuration, under any name you like as I have done with the logger key, which contains information about how to configure a Monolog instance.

Retrieving settings

The settings are stored in the DI container so you can access them via the settings key in container factories. For example, I can set up a container factory for to create my Monolog instance like this:

$container = $app->getContainer();
$container['logger'] = function ($c) {
    $settings = $c->get('settings')['logger'];
    $logger = new Monolog\Logger($settings['name']);
    $logger->pushProcessor(new Monolog\Processor\UidProcessor());
    $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
    return $logger;
};

Slim’s default container is Pimple, so we use the array notation to register a new service called ‘logger’ in this case. The logger settings are retrieved using $settings = $c->get('settings')['logger'].

Separate file for configuration

You should use a separate file for your configuration. This is most easily done using require. Firstly, we create config.php and return the configuration array from it:

config.php:

<?php

return [
    'settings' => [
        'displayErrorDetails' => true,

        'logger' => [
            'name' => 'slim-app',
            'level' => Monolog\Logger::DEBUG,
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ]
];

As require returns whatever the included file returns, we load like this:

$config = require 'config.php';
$app = new \Slim\App($config);

Environment variables using dotenv

Consider using environment variables for the differences between servers. These can be configured as part of your apache/nginx setup or you can use dotenv.

In this case, create a .env file and add it to .gitignore:

.env:

DISPLAY_ERRORS=0
LOG_LEVEL=400

We can use these environment variables in our configuration file via getenv:

config.php:

<?php

return [
    'settings' => [
        'displayErrorDetails' => (bool)getenv('DISPLAY_ERRORS'),

        'logger' => [
            'name' => 'slim-app',
            'level' => (int)getenv('LOG_LEVEL') ?: 400,
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ]
];

We then load using:

$dotenv = new Dotenv\Dotenv(__DIR__);
$dotenv->load();
$config = require 'config.php';
$app = new \Slim\App($config);

Multiple configuration files

You may also want to split your configuration into multiple files which are then merged together with files loaded later overriding the settings from previous files.

One use case is to have a local.config.php that’s not in git that contains per-server configuration to be merged with your master configuration in config.php.

For example, your live configuration should turn off display of errors and maybe set a different logging level:

local.config.php:

<?php
return [
    'settings' => [
        'displayErrorDetails' => false,

        'logger' => [
            'level' => Monolog\Logger::ERROR,
        ],
    ]
];

Merging the two arrays is a little complicated as array_merge_recursive doesn’t do what you expect and will result in the 'displayErrorDetails' key becoming an array with two elements. You could write your own merge method, but it’s easier to use zend-stdlib‘s merge method:

$config = require 'config.php';
$localConfig = require 'local.config.php';
$config = Zend\Stdlib\ArrayUtils::merge($config, $localConfig);
$app = new \Slim\App($config);

Ini/Yaml/JSON/XML configuration

If you want to use something other than PHP arrays, then use zend-config along with glob. In this case, you place all your configuration files in a single directory, such as config/ and then name them with .global.{type} and .local.{type} to control order.

For example, to load the configuration files global.yaml, db.global.yaml and then local.yaml in that order:

$config = Zend\Config\Factory::fromFiles(glob('config/{global, *.global, local}.*', GLOB_BRACE));

One useful feature of zend-config is that you can mix and match between formats, so local.yaml could be local.ini and it would still work.

Summary

As you can see, using configuration with Slim is very easy; all the choices come down to how you want to organise your configuration so that you can manage the differences between environments easily.

One thought on “Configuration in Slim Framework

  1. Hello Rob,

    Thanks for this article (and many others). I just started using Slim and being used ZF2's suggested config loading strategy, I wanted to do what you explain here. Just a quick note that I had to debug your glob example because it seems spaces in {global, *.global, local} should be removed or it's going to look for actual spaces in the file name. It could not load my local.php and I was wondering why, that was the reason.

    Thanks again.

Comments are closed.