Configula
A simple PHP configuration reader that supports local config files and favors convention over configuration
Install / Use
/learn @caseyamcl/ConfigulaREADME
Configula
Configula is a configuration library for PHP 8.2+ (older versions supported; see below).
[![Latest Version on Packagist][ico-version]][link-packagist] ![Software License][ico-license] [![Github Build][ico-ghbuild]][link-ghbuild] ![Code coverage][ico-coverage] [![PHPStan Level 5][ico-phpstan]][link-phpstan] [![Total Downloads][ico-downloads]][link-downloads]
Use this library when you want to load configuration from the filesystem, environment, and other sources. It implements your configuration values as an immutable object in PHP. It is a framework-independent tool, and can be easily used in any PHP application.
Features
- Load configuration from a variety of sources:
- Cascade/deep merge values from multiple sources (e.g. array, files, environment, etc)
- Optionally use in combination with Symfony Config Component to validate configuration values and/or cache them
- Creates an immutable object to access configuration values in your application:
- Array-access (read-only)
get(val),has(val), andhasValue(val)methods- Magic methods (
__get(val),__isset(val), and__invoke(val)) - Implements
TraversableandCountableinterfaces
- Provides simple dot-based access to nested values (e.g.
$config->get('application.sitename.prefix');) - Code quality standards: PSR-12, complete unit test coverage
Installation
composer require caseyamcl/configula
Upgrading?
Refer to UPGRADE.md for notes on upgrading from Version 2.x, 3.x, or 4.x to v5.
Need PHP v7.x, v5.x, or older Symfony compatibility?
Configula v5.x is compatible with PHP v8.2+.
- If you need PHP 7.3+ compatibility, instruct Composer to use the 4.x version of this library:
composer require caseyamcl/configula:^4.2 - If you need PHP 7-7.2+ compatibility, instruct Composer to use the 3.x version of this library:
composer require caseyamcl/configula:^3.2 - If you need PHP 5.x support, you can use the 2.x version of this library (no longer maintained):
composer require caseyamcl/configula:^2.4
Loading Configuration
You can use the Configula\ConfigFactory to load configuration from files, the environment, or other sources:
use Configula\ConfigFactory as Config;
// Load all .yml, .php, .json, and .ini files from directory (recursive)
// Supports '.local' and '.dist' modifiers to load config in correct order
$config = Config::loadPath('/path/to/config/files', ['optional' => 'defaults', ...]);
// Load all .yml, .php, .json, and .in files from directory (non-recursive)
// Supports '.local' and '.dist' modifiers to load config in correct order
$config = Config::loadSingleDirectory('/path/to/config/files', ['optional' => 'defaults', ...]);
// Load from array
$config = Config::fromArray(['some' => 'values']);
// Chain loaders -- performs deep merge
$config = Config::fromArray(['some' => 'values'])
->merge(Config::loadPath('/some/path'))
->merge(Config::loadEnv('MY_APP'));
Or, if you are loading an array, you can instantiate Configula\ConfigValues directly:
$config = new Configula\ConfigValues(['array' => 'values']);
Or, you can manually invoke any of the loaders in the Configula\Loader namespace:
$config = (new Configula\Loader\FileListLoader(['file-1.yml', 'file-2.json']))->load();
Accessing Values
The Configula\ConfigValues object provides several ways to access your configuration values:
// get method - throws exception if value does not exist
$config->get('some_value');
// get method with default - returns default if value does not exist
$config->get('some_value', 'default');
// find method - returns NULL if value does not exist
$config->find('some_value');
// has method - returns TRUE or FALSE
$config->has('some_value');
// hasValue method - returns TRUE if value exists and is not empty (NULL, [], "")
$config->hasValue('some_value');
Accessing values using dot notation
Configula supports accessing values via dot-notation (e.g some.nested.var):
// Here is a nested array:
$values = [
'debug' => true,
'db' => [
'platform' => 'mysql',
'credentials' => [
'username' => 'some',
'password' => 'thing'
]
],
];
// Load it into Configula
$config = new \Configula\ConfigValues($values);
// Access top-level item
$values->get('debug'); // bool; TRUE
// Access nested item
$values->get('db.platform'); // string; 'mysql'
// Access deeply nested item
$values->get('db.credentials.username'); // string: 'some'
// Get item as array
$values->get('db'); // array ['platform' => 'mysql', 'credentials' => ...]
// has/hasValue work too
$values->has('db.credentials.key'); // false
$values->hasValue('db.credentials.key'); // false
Property-like access to your config settings via __get() and __isset():
// Access configuration values
$config = Config::loadPath('/path/to/config/files');
// Throws exception if value does not exist
$some_value = $config->some_key;
// Returns TRUE or FALSE
isset($config->some_key);
Iterator and count access to your config settings:
// Basic iteration
foreach ($config as $item => $value) {
echo "<li>{$item} is {$value}</li>";
}
// Count
count($config); /* or */ $config->count();
Callable access to your config settings via __invoke():
// Throws exception if value does not exist
$value = $config('some_value');
// Returns default value if value does not exist
$value = $config('some_value', 'default');
Array access to your config settings:
// Throws exception if value does not exist
$some_value = $config['some_key'];
// Returns TRUE or FALSE
$exists = isset($config['some_key']);
// Not allowed; always throws exception (config is immutable)
$config['some_key'] = 'foobar'; // Configula\Exception\ConfigLogicException
unset($config['some_key']); // Configula\Exception\ConfigLogicException
Merging Configuration
Since Configula\ConfigValues is an immutable object, you cannot mutate the configuration
once it is set. However, you can merge values and get a new copy of the object using the merge
or mergeValues methods:
use Configula\ConfigValues;
$config = new ConfigValues(['foo' => 'bar', 'baz' => 'biz']);
// Merge configuration using merge()
$newConfig = $config->merge(new ConfigValues(['baz' => 'buzz', 'cad' => 'cuzz']));
// For convenience, you can pass in an array using mergeValues()
$newConfig = $config->mergeValues(['baz' => 'buzz', 'cad' => ['some' => 'thing']]);
Configula performs a deep merge. Nested arrays are traversed, and the last value always takes precedence.
Note that Configula does not deep merge nested objects, only arrays.
Iterator and Count
The built-in ConfigValues::getIterator() and ConfigValues::count() methods flattens nested values when iterating
or counting:
// Here is a nested array
$config = new Configula\ConfigValues([
'debug' => true,
'db' => [
'platform' => 'mysql',
'credentials' => [
'username' => 'some',
'password' => 'thing'
]
],
]);
// ---------------------
foreach ($config as $path => $value) {
echo "\n" . $path . ": " . $value;
}
// Output:
//
// debug: 1
// db.platform: mysql
// db.credentials.username: some
// db.credentials.password: thing
//
echo count($config);
// Output: 4
If you want to iterate only the top-level items in your configuration, you can use the getArrayCopy() method:
foreach ($config->getArrayCopy() as $path => $value) {
echo "\n" . $path . ": " . $value;
}
// Output:
//
// debug: 1
// db: Array
//
Using the Folder Loader - Config Folder Layout
The folder loaders in Configula will load files with the following extensions (you can add your own custom loaders; see below):
php- Configula will look for an array called$configin this file.json- Uses the built-in PHPjson_decode()functionyamloryml- Uses the Symfony YAML parserini- Uses the built-in PHPparse_ini_file()function
The ConfigFactory::loadPath($path) method will traverse directories in your configuration path recursively.
The ConfigFactory::loadSingleDirectory($path) method will load your configuration in a single directory
non-recursively.
Local Configuration Files
In some cases, you may want to have local configuration files that override the default configuration files. There are two ways to do this:
- prefix the default configuration file extension with
.dist(e.g.config.dist.yml), and name the local configuration file normally:config.yml - name the default configuration file normally (e.g.
config.yml) and prefix.localto the extension for the local configuration file:config.local.yml.
Either way will work, and you could even combine approaches if you want. The file iterator will always cascade merge files in this order:
FILENAME.dist.EXTFILENAME.EXTFILENAME.local.EXT
This is useful if you want to keep local configuration files out of revision control. Choose a paradigm,
and simply add the following to your .gitignore
# If keeping .dist files...
[CONFIGDIR]/*
[!CONFIGDIR]/*.dist.*
# or, if ignoring .local files...
[CONFIGDIR]/*.local.*
Example
Consider the following directory layout...
/my/app/config
├config.php
├config.di
