popphp / pop-config
Pop Config Component for Pop PHP Framework
Requires
- php: >=8.4.0
- popphp/pop-utils: ^3.0.0
- symfony/yaml: ^6.4 || ^7.0 || ^8.0
Requires (Dev)
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^12.5.0
Suggests
- ext-dom: For handling DOM data
- ext-json: For handling JSON data
- ext-simplexml: For handling XML data
README
- Overview
- Install
- Quickstart
- Set and access values
- Access nested values with dot notation
- Allow changes
- Merge new values into the config object
- Convert config object down to a basic array
- Convert config object to an
ArrayObject - Parse a configuration file
- Render config data to a string format
- Write config data to a file
- A note on YAML support
- Exceptions
Overview
pop-config is a basic configuration component that helps centralize application
configuration values and parameters. Values can be accessed via array notation,
object arrow notation, or dot notation for nested values. It can disable changes
to the configuration values if need be for the life-cycle of the application. It
also can parse configuration values from common formats, such as JSON, XML, INI
and YAML.
pop-config is a component of the Pop PHP Framework.
Install
Install pop-config using Composer.
composer require popphp/pop-config
Or, require it in your composer.json file
"require": {
"popphp/pop-config" : "^5.0.0"
}
Quickstart
Set and access values
use Pop\Config\Config; $config = new Config(['foo' => 'bar']); $foo = $config->foo; // OR $foo = $config['foo'];
Access nested values with dot notation
Nested values can also be accessed directly with dot notation, without manually chaining array access at each level.
use Pop\Config\Config; $config = new Config(['database' => ['host' => 'localhost', 'port' => 5432]]); $host = $config['database.host']; // OR $host = $config->{'database.host'};
A literal key always takes priority over dot-path traversal — a key that happens
to contain a . (e.g. 'example.com') is matched exactly first, and only falls
back to nested traversal when no literal key matches.
Setting and unsetting values also support dot notation, when changes are allowed:
use Pop\Config\Config; $config = new Config(['database' => ['host' => 'localhost']], true); $config['database.port'] = 5432; // $config->toArray() is now ['database' => ['host' => 'localhost', 'port' => 5432]] unset($config['database.host']); // removes just the 'host' key, leaving ['database' => ['port' => 5432]]
Note: setting a brand-new dotted key — one that doesn't already exist as a
literal key — always creates a nested structure, not a literal key.
$config['example.com'] = 'x' on an empty config produces
['example' => ['com' => 'x']], not ['example.com' => 'x']. A literal key only
"wins" when it already exists in the underlying data (e.g. loaded from a file).
Allow changes
Changes to configuration values are disabled by default. Attempting to set or
unset a value on a config that doesn't allow changes throws a
Pop\Config\ChangesNotAllowedException. Check whether a config allows changes
with changesAllowed().
use Pop\Config\Config; $config = new Config(['foo' => 'bar'], true); $config->foo = 'New Value'; $config->changesAllowed(); // true
Merge new values into the config object
By default, incoming values overwrite existing ones on a collision:
use Pop\Config\Config; $config = new Config($configData, true); $config->merge($newData);
Pass true as the second argument to preserve existing values instead — on a
scalar collision, the existing value wins, and two colliding list values are
kept as the original list wholesale rather than spliced together.
$config->merge($newData, true);
Note: when a list value collides with an associative array value at the
same key (in either merge mode), the two are combined into a hybrid array
rather than one side winning outright. For example, merging existing
['a' => ['x', 'y', 'z']] with incoming ['a' => ['one' => 1]] produces
['a' => ['x', 'y', 'z', 'one' => 1]] in both default and preserve: true
modes. This asymmetric case is a known limitation — avoid mixing list and
associative shapes at the same key across merges if you need predictable
results.
mergeFromData() merges directly from a file path (or anything parseData()
accepts), parsing it first:
$config->mergeFromData('/path/to/other-config.json'); // OR, preserving existing values on collision $config->mergeFromData('/path/to/other-config.json', true);
Both merge() and mergeFromData() throw Pop\Config\ChangesNotAllowedException
if the config doesn't allow changes; mergeFromData() also throws
Pop\Config\ParseException/Pop\Config\UnsupportedFormatException if the file
can't be read or parsed.
Convert config object down to a basic array
use Pop\Config\Config; $config = new Config($configData); $data = $config->toArray();
Convert config object to an ArrayObject
use Pop\Config\Config; $config = new Config($configData); $arrayObject = $config->toArrayObject(); // Pop\Utils\ArrayObject $nativeArrayObject = $config->toArrayObject(true); // native \ArrayObject, ARRAY_AS_PROPS
Both are built from a fresh copy of the data — mutating the returned object never
affects the original Config.
Parse a configuration file
; This is a sample configuration file config.ini
[foo]
bar = 1
baz = 2
use Pop\Config\Config; $config = Config::createFromData('/path/to/config.ini'); // $value equals 1 $value = $config->foo['bar']; // OR $value = $config['foo']['bar'];
Render config data to a string format
Supported formats include PHP, JSON, XML, INI and YAML
use Pop\Config\Config; $config = new Config($configData); echo $config->render('json');
Write config data to a file
writeToFile() picks the format from the filename's extension and writes the
rendered output directly to disk:
use Pop\Config\Config; $config = new Config($configData); $config->writeToFile('/path/to/config.json');
Supported extensions are the same five formats as render() — an unsupported
extension throws Pop\Config\UnsupportedFormatException. Note: a filename
with no extension at all (no . in it) silently does nothing — no file is
written and no exception is thrown.
A note on YAML support
YAML parsing and rendering go through
symfony/yaml (a
required dependency, not an optional PHP extension). Two scalar-parsing
differences from the previous PECL yaml extension are worth knowing about:
- Boolean words (
yes/no/on/off/y/n, any casing) and octal-looking integers (e.g.0755) are normalized back tobool/intto match the old behavior — no action needed. - Bare (unquoted) dates are not normalized. A YAML value like
released: 2001-01-23parses as a Unix timestampint, not a string. If you need the literal string, quote it in the YAML file:released: "2001-01-23".
Exceptions
All exceptions thrown by pop-config extend the base Pop\Config\Exception, so
existing code catching that class continues to work. More specific subclasses are
available for finer-grained handling:
Pop\Config\ChangesNotAllowedException— thrown by__set(),__unset(),merge(), andmergeFromData()when the config doesn't allow changes.Pop\Config\ParseException— thrown bycreateFromData()/parseData()for a missing file, unparseable content, or invalid input.Pop\Config\UnsupportedFormatException— thrown bycreateFromData()/parseData()for an unrecognized file extension, and byrender()for an unrecognized format string.
use Pop\Config\Config; use Pop\Config\ParseException; try { $config = Config::createFromData('/path/to/config.yml'); } catch (ParseException $e) { // handle a missing file or malformed content specifically }
use Pop\Config\Config; use Pop\Config\ChangesNotAllowedException; $config = new Config(['foo' => 'bar']); // changes not allowed (the default) try { $config->foo = 'baz'; } catch (ChangesNotAllowedException $e) { // handle the immutability violation specifically }