A PHP Persistent Configurations Container
A PHP Persistent Configuration Container
Yaconf is a configuration container. It parses INI files and stores the result in PHP at startup. Configurations live in persistent memory across the entire PHP lifecycle, which makes it very fast.
Yaconf uses an immutable data + Copy-on-Write design rather than shared memory (shmget/mmap). Parsed configs are stored in persistent zend_arrays marked IS_ARRAY_IMMUTABLE — and all keys are interned as permanent strings. Because the hash tables are immutable, PHP-FPM workers forked from the master process share the same physical memory pages via the OS kernel's COW mechanism. As long as the configuration doesn't change, memory is allocated only once — no matter how many workers are running. When a config file is modified and Yaconf reloads it (in non-ZTS mode), the kernel copies only the changed pages on write, isolating the new config from the old.
Since 1.2.0, once parsing finishes Yaconf compacts the whole config tree into a single contiguous block (two-phase compaction): it walks the parsed tree, collects every string and hash table, allocates one block, and copies them in — deduplicating strings by content and re-laying out every hash table into canonical engine layout. All the scattered per-node allocations from the parse phase are freed. The win is twofold: fewer individual allocations means lower memory overhead, and a contiguous layout means better cache locality and fewer touched pages when workers COW the config. When a config file changes, Yaconf rebuilds and re-compacts from scratch; tables that need to grow are detached onto the persistent heap first so the engine can resize them without touching the block.
⚠ ZTS (Thread-Safe) builds: Yaconf loads configurations at startup as usual, but automatic reloading is not available (
yaconf.check_delayis NTS-only). Restart PHP to pick up config changes.
Most PHP applications have a pile of .ini or .php config files that get parsed on every request. Every request pays the I/O and parse cost, then throws the result away — only to do it again on the next request.
Yaconf flips this: parse once at startup, serve from memory forever. The parsed config lives in persistent zend_arrays with immutable hash tables. Yaconf::get() is a pure hash lookup — no file I/O, no parsing, no memory allocation per request.
include or parse_ini_file() on every request today..ini files beyond what your yaconf.directory contains.Yaconf is for static configuration. For runtime caching — database query results, computed data, HTML fragments, ephemeral tokens — use Yac, which shares the same "local first, zero dependency" design philosophy.
sub/x.ini is addressed as "sub.x". Sub-directories are tracked for hot reload too.Yaconf::__debug_info() now reports the stored value's address.sub/x.ini is addressed as "sub.x" (since 1.2.0)Yaconf is a PECL extension, simply install it by:
$ pecl install yaconfYaconf can also be installed with PIE, the PHP Installer for Extensions:
$ pie install laruence/yaconf$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config
$ make && make install| INI Setting | Default | Description |
|---|---|---|
yaconf.directory |
"" |
Path to the directory where all INI configuration files are placed. Sub-directories are loaded recursively (up to 16 levels deep). |
yaconf.check_delay |
300 |
Interval in seconds at which Yaconf checks for config file changes (by comparing directory mtimes — first the configured directory, then each tracked sub-directory). Set to 0 to check on every request. Only available in non-ZTS builds. In ZTS builds, configurations are still loaded at startup, but automatic reloading is disabled — restart PHP to pick up changes. |
Yaconf does not register any PHP constants.
All Yaconf methods are static — you call them on the class directly, not on an instance.
static mixed Yaconf::get(string $name, mixed $default = null)Fetches a configuration value by its $name. The $name uses dot notation to traverse nested keys (e.g. "foo.name", "foo.features.1", "sub.x.role"). The maximum nesting depth is 64.
Returns the configuration value on success, or $default (which defaults to null) if the key is not found.
static bool Yaconf::has(string $name)Returns true if a configuration value exists at $name, false otherwise.
string(6) "yaconf"
["year"]=>
string(4) "2015"
["features"]=>
array(5) {
[0]=>
string(4) "fast"
[1]=>
string(5) "light"
["plus"]=>
string(9) "zero-copy"
["constant"]=>
string(9) "7.0.0-dev"
["env"] =>
string(16) "/home/huixinchen"
}
}
*/As you can see, Yaconf supports string, map (array), INI section inheritance, environment variables, and PHP constants.
You can also access configurations using dot notation:
$ php -r 'var_dump(Yaconf::get("foo.name"));'
// string(6) "yaconf"
$ php -r 'var_dump(Yaconf::get("foo.features.1"));'
// string(5) "light"
$ php -r 'var_dump(Yaconf::get("foo.features")["plus"]);'
// string(9) "zero-copy"Now let's see sections and section inheritance:
$ php -r 'var_dump(Yaconf::get("bar"));'
/*
array(2) {
["base"]=>
array(2) {
["parent"]=>
string(6) "yaconf"
["children"]=>
string(4) "NULL"
}
["children"]=>
array(2) {
["parent"]=>
string(6) "yaconf"
["children"]=>
string(3) "set"
}
}
*/The children section inherits values from the base section, and can override the values it wants to change.
Sub-directory support is available since 1.2.0.
Now assume /tmp/yaconf also contains sub-directories:
/tmp/yaconf/
├── foo.ini
├── bar.ini
└── sub/
├── x.ini ; role="assistant"
└── deep/
└── y.ini ; level="three"Each sub-directory becomes a key level; files inside it are addressed with the directory name as a prefix — at any depth:
$ php -r 'var_dump(Yaconf::get("sub.x.role"));'
// string(9) "assistant"
$ php -r 'var_dump(Yaconf::get("sub.deep.y.level"));'
// string(5) "three"Fetching a directory name alone returns the whole directory as an array:
$ php -r 'var_dump(array_keys(Yaconf::get("sub")));'
/*
array(2) {
[0]=>
string(4) "deep"
[1]=>
string(1) "x"
}
*/A .ini file and a directory with the same name would claim the same key; the directory wins — Yaconf emits a warning and skips the file.
When yaconf.check_delay is non-zero, Yaconf adds a block to phpinfo() showing the directory being watched, the configured check delay, a list of all currently loaded .ini files (with their path relative to yaconf.directory) and their last modification time, plus a list of all tracked sub-directories.
No open issues yet, or sync has not completed.