Lightweight library for merging and caching application config
PHP
Latest commit bde7d5e Mar 17, 2016 @mtymek update changelog for release

README.md

Expressive Configuration Manager

Build Status

Lightweight library for collecting and merging configuration from different sources.

It is designed for zend-expressive applications, but it can work with any PHP project.

Usage

Config files

At the basic level, ConfigManager can be used to merge PHP-based configuration files:

use Zend\Expressive\ConfigManager\ConfigManager;
use Zend\Expressive\ConfigManager\PhpFileProvider;

$configManager = new ConfigManager(
    [
        new PhpFileProvider('*.global.php'),
    ]
);

var_dump($configManager->getMergedConfig());

Each file should return plain PHP array:

// db.global.php
return [
    'db' => [
        'dsn' => 'mysql:...',
    ],    
];

// cache.global.php
return [
    'cache_storage' => 'redis',
    'redis' => [ ... ],
];

Result:

array(3) {
  'db' =>
  array(1) {
    'dsn' =>
    string(9) "mysql:..."
  }
  'cache_storage' =>
  string(5) "redis"
  'redis' =>
  array(0) {
     ...
  }
}

Configuration is merged in the same order as it is passed, with later entries having precedence.

Config providers

ConfigManager works by aggregating "Config Providers" passed when creating object. Each provider should be a callable, returning configuration array (or generator) to be merged.

$configManager = new ConfigManager(
    [
        function () { return ['foo' => 'bar']; },
        new PhpFileProvider('*.global.php'),
    ]
);
var_dump($configManager->getMergedConfig());

If provider is a class name, it is automatically instantiated. This can be used to mimic ZF2 module system - you can specify list of config classes from different packages, and aggregated configuration will be available to your application. Or, as a library owner you can distribute config class with default values.

Example:

class ApplicationConfig
{
    public function __invoke()
    {
        return ['foo' => 'bar'];
    }
}

$configManager = new ConfigManager(
    [
        ApplicationConfig::class,
        new PhpFileProvider('*.global.php'),
    ]
);
var_dump($configManager->getMergedConfig());

Output from both examples will be the same:

array(4) {
  'foo' =>
  string(3) "bar"
  'db' =>
  array(1) {
    'dsn' =>
    string(9) "mysql:..."
  }
  'cache_storage' =>
  string(5) "redis"
  'redis' =>
  array(0) {
  }
}

Caching

In order to enable configuration cache, pass cache file name as a second parameter to ConfigManager constructor:

$configManager = new ConfigManager(
    [
        function () { return [ConfigManager::ENABLE_CACHE => true]; },
        new PhpFileProvider('*.global.php'),
    ],
    'data/config-cache.php'
);

When cache file is specified, you will also need to add config_cache_enabled key to the config, and set it to TRUE (use ConfigManager::ENABLE_CACHE constant for convenience). This allows to enable cache during deployment by simply putting extra *.local.php file in config directory:

return [
    ConfigManager::ENABLE_CACHE` => true,
];

When caching is enabled, ConfigManager does not iterate config providers. Because of that it is very fast, but after it is enabled you cannot do any changes to configuration without clearing the cache. Caching should be used only in production environment, and your deployment process should clear the cache.

Generators

Config providers can be written as generators. This way single callable can provide multiple configurations:

$configManager = new ConfigManager(
    [
        function () { 
            foreach (Glob::glob('data/*.global.php', Glob::GLOB_BRACE) as $file) {
                yield include $file;
            } 
        }        
    ]
);
var_dump($configManager->getMergedConfig());

PhpFileProvider is implemented using generators.

Available config providers

PhpFileProvider

Loads configuration from PHP files returning arrays, like this one:

return [
    'db' => [
        'dsn' => 'mysql:...',
    ],    
];

Wildcards are supported:

$configManager = new ConfigManager(
    [
        new PhpFileProvider('config/*.global.php'),        
    ]
);

Example above will merge all matching files from config directory - if you have files like app.global.php, database.global.php, they will be loaded using this few lines of code.

Internally, ZendStdlib\Glob is used for resolving wildcards, meaning that you can use more complex patterns (for instance: 'config/autoload/{{,*.}global,{,*.}local}.php'), that will work even on Windows platform.

ZendConfigProvider

Sometimes using plain PHP files may be not enough - you may want to build your configuration from multiple files of different formats: INI, YAML, or XML. For this purpose you can leverage ZendConfigProvider:

$configManager = new ConfigManager(
    [
        new ZendConfigProvider('*.global.json'),
        new ZendConfigProvider('database.local.ini'),
    ]
);

ZendConfigProvider accepts wildcards and autodetects config type based on file extension.

ZendConfigProvider requires two packages to be installed: zendframework/zend-config and zendframework/zend-servicemanager. Some config readers (JSON, YAML) may need additional dependencies - please refer to Zend Config Manual for more details.