Config Objects
pop-config wraps configuration data in an object that reads like an array, an object or a dot path,
loads from five file formats, and refuses writes unless you ask for them. Pop\Application takes one
directly, so the config an application boots with can be a Config instance rather than a bare array.
composer require popphp/pop-config
Creating a Config Object#
The constructor takes an array and gives you three ways to read it back:
use Pop\Config\Config;
$config = new Config([
'foo' => 'bar',
'database' => ['host' => 'localhost', 'port' => 5432],
]);
$config->foo; // 'bar'
$config['foo']; // 'bar'
$config['database.host']; // 'localhost'
$config->{'database.port'}; // 5432
Dot notation walks nested values without chaining array access at each level, and isset() understands
it too. A literal key is matched first: if the data has an actual 'example.com' key, that key wins over
traversing into an example array.
Config::createFromData() loads from a file instead, picking the parser from the extension:
use Pop\Config\Config;
$config = Config::createFromData(__DIR__ . '/../app/config/database.json');
$config['database.host'];
A path that does not exist throws Pop\Config\ParseException ("Error: The config file '...' does not
exist."), and content that will not parse throws the same class ("Error: Unable to parse the config data
from '...'."). An extension outside the five supported ones throws
Pop\Config\UnsupportedFormatException.
createFromData() and parseData() take a file path. For config text you already hold, decode it and
pass the array to the constructor.
Two conversions get the data back out. toArray() returns a plain array; toArrayObject() returns a
Pop\Utils\ArrayObject, or a native \ArrayObject with ARRAY_AS_PROPS when passed true:
use Pop\Config\Config;
$config = new Config(['foo' => 'bar']);
$data = $config->toArray();
$arrayObject = $config->toArrayObject();
$nativeArrayObject = $config->toArrayObject(true);
Both objects are built from a fresh copy, so mutating one never reaches back into the Config.
Supported Formats#
Five formats parse and render: PHP, JSON, INI, XML and YAML. render() takes the format name and returns
a string; writeToFile() picks the format from the filename's extension and writes it.
use Pop\Config\Config;
$config = new Config(['foo' => ['bar' => 1, 'baz' => 2]]);
echo $config->render('json');
$config->writeToFile(__DIR__ . '/../app/config/generated.json');
toJson(), toYaml(), toIni() and toXml() are the named equivalents of render(). An unrecognized
format string or extension throws Pop\Config\UnsupportedFormatException ("Invalid type 'toml'.
Supported config file types are PHP, JSON, YAML, INI or XML.").
The five are not equally faithful. PHP, JSON and YAML round-trip a nested array unchanged, types included; XML and INI do not.
| Format | Extensions | Round-trips a nested array |
|---|---|---|
| PHP | .php |
Yes, types preserved |
| JSON | .json |
Yes, types preserved |
| YAML | .yaml, .yml |
Yes, types preserved |
| XML | .xml |
Structure yes, but every scalar comes back a string |
| INI | .ini |
One level, types preserved — see the note below |
INI flattens as it round-trips: render('ini') writes a nested array as an [foo] section whose keys
repeat the section name. Use JSON or YAML when you need nesting preserved.
The YAML parser is symfony/yaml, a required dependency rather than a PHP extension, so YAML support is
always available. Boolean words (yes, no, on, off, y, n, in any casing) and octal-looking
integers such as 0755 are normalized to bool and int. One scalar is not normalized:
released: 2001-01-23
quoted: "2001-01-23"
released parses to the integer 980208000 — a Unix timestamp, not a string. quoted parses to the
string '2001-01-23'. Quote any bare date you want to keep as text.
Merging and Immutability#
A Config is read-only by default. Setting or unsetting a value on one throws
Pop\Config\ChangesNotAllowedException ("Real-time configuration changes are not allowed."), which is
the point — configuration read at boot should not drift underneath the code that read it.
use Pop\Config\ChangesNotAllowedException;
use Pop\Config\Config;
$config = new Config(['foo' => 'bar']);
$config->changesAllowed(); // false
try {
$config->foo = 'baz';
} catch (ChangesNotAllowedException $e) {
// nothing was written
}
Pass true as the constructor's second argument for a config you intend to modify. Dot notation works
for writes as well as reads:
use Pop\Config\Config;
$config = new Config(['database' => ['host' => 'localhost']], true);
$config['database.port'] = 5432; // ['database' => ['host' => 'localhost', 'port' => 5432]]
unset($config['database.host']); // ['database' => ['port' => 5432]]
A dotted key that does not already exist always creates nesting rather than a literal key —
$config['example.com'] = 'x' on an empty config produces ['example' => ['com' => 'x']]. A literal key
only wins when it is already in the data, having been loaded from a file that had it.
merge() folds another array in. By default the incoming values overwrite on a collision; pass true as
the second argument and the existing values survive instead:
use Pop\Config\Config;
$config = new Config(['x' => 1, 'y' => ['a' => 1]], true);
$config->merge(['x' => 2, 'y' => ['b' => 2]]);
// ['x' => 2, 'y' => ['a' => 1, 'b' => 2]]
$config->merge(['x' => 99], true);
// x stays 2 — the existing value is preserved
mergeFromData() does the same from a file path, parsing it first. Both throw
ChangesNotAllowedException on a config that does not allow changes, and mergeFromData() adds the
ParseException and UnsupportedFormatException cases from loading.
Keep a key's shape consistent across the configs you merge — a list meeting an associative array at the same key produces a hybrid in both merge modes.
Where this fits in an application: config files hold data, and the application class consumes it.
A route table, connection credentials and service definitions belong in app/config/; database
initialization and service registration belong in load() on your Pop\Application subclass. A Config
object is what carries the first across that line.
Rendering options, the to*() family and the full merge semantics go past what this page covers — see
the pop-config README.
See Also#
- Configuration — where an application's config files live and how they load
- Applications & Bootstrap — the
load()method that consumes this data - Utilities & Helpers —
Pop\Utils\ArrayObject, whattoArrayObject()returns - pop-config README — every method and exception