Library / SDK
laruence/yaconf avatar
laruence/yaconf

Yaconf parses your configuration once and keeps it in shared memory forever

Ultra-fast, Secure, and Persistent Configuration Management for PHP

1,046 stars178 forksPHPNOASSERTION

At a glance

What is it?
Yaconf is a PHP extension written in C that reads an INI or YAML configuration tree at startup and serves it from persistent memory for the rest of the process lifetime. The interesting part is the security model it buys: the files can live in a root-only directory outside the web root, because only the master process ever reads them.
Who is it for?
Adopt Yaconf when a PHP application parses the same configuration on every request and you want that cost removed, especially when the configuration holds credentials you would rather keep in a directory the web user cannot read. Do not adopt it for runtime caching, because the readme points at Yac for query results and ephemeral tokens, and do not adopt it if you depend on live configuration reloads on ZTS, since ZTS builds never reload.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Parse once, serve forever

Yaconf is a configuration container. It parses INI files by default, with optional YAML support backed directly by libyaml, and stores the result in persistent memory at startup, where it stays for the entire PHP lifecycle.

The comparison the readme makes is the one to hold on to. Most PHP applications parse their configuration on every request, while Yaconf does it once at startup and serves from memory forever. Yaconf::get() is a pure hash lookup, with no file I/O, no parsing, and no per-request allocation.

The memory story is the second half of the design. The parsed config lives in persistent, immutable zend_arrays, so PHP-FPM workers forked from the master share the same physical memory pages through the kernel's copy-on-write mechanism. Memory is allocated once no matter how many workers run.

Since 1.2.0 the whole tree is also compacted into a single contiguous block, with strings deduplicated and hash tables re-laid out, which the readme says cuts memory overhead and improves cache locality.

The workload it is designed for is stated explicitly. Yaconf stores static configuration, values read often but changed rarely, like credentials, feature flags and routing tables, and it resolves INI constants and environment variables once at parse time rather than on access.

The requirement is PHP 7 or newer, and the implementation is C, with the extension living in a single yaconf.c file next to its build configuration.

The security model is a consequence of who reads the files

The Secure section is the part of the readme that is worth reading twice, because the security property is a side effect of the performance design rather than a feature bolted on.

Yaconf reads the configuration directory once, at startup. Under PHP-FPM that read happens in the master process, which normally runs as root, and the workers drop to the pool user only after forking. Requests are then served entirely from memory, so a worker never reopens those files.

The consequence is that permission separation becomes possible. Configuration can sit outside the web root, in a root-owned directory the pool user cannot read, and application code only ever calls Yaconf::get(), never the files themselves, so a file-disclosure or file-inclusion vulnerability cannot reach the credentials stored there.

That is a real property, and it is the kind of thing that is hard to retrofit later. A conventional config file has to be readable by the web user, and a local file inclusion bug in any script is then a credential leak. Here the file is unreadable to the process that runs the vulnerable code.

The trade-off is stated in bold in the readme as the hot reload. Reloading runs in the worker, at RINIT, and a worker that cannot read the directory just keeps serving the configuration loaded at startup. Picking up a change means restarting or gracefully reloading PHP-FPM, and a reload re-executes the master as root, so MINIT runs again and the new configuration is read.

So you choose between the two. If live reload matters more than the permission separation, make the directory readable by the pool user and tune yaconf.check_delay. And note the trap in the setting, that 0 means re-check on every request, which is the most eager setting rather than a way of disabling reloading. ZTS builds never reload at all.

Three ways to install, and the libyaml caveat

Yaconf is a PECL extension, and the shortest install is one command.

bash
$ pecl install yaconf

Since 1.2.0 it can also be installed with PIE, the PHP Installer for Extensions.

bash
$ pie install laruence/yaconf

And it can be compiled from source, which is what you need if you want YAML support or a custom build.

bash
$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config --with-yaml
$ make && make install

The YAML flag links directly against the system libyaml, and there is a variant that takes a libyaml installation prefix instead.

The requirement for YAML support is matching libyaml headers and libraries, and the readme is specific about the failure mode on Windows. The --with-yaml option is enabled only when matching libyaml development inputs are available, and otherwise the build warns and remains INI-only. That is a good failure mode, since a build that silently ignored YAML files would give you an application that starts with missing configuration and no obvious cause.

The runtime configuration table has two settings. yaconf.directory is the path to the directory holding the configuration files, where .ini is always supported and .yaml and .yml require a build configured with --with-yaml, and where sub-directories are loaded recursively up to 16 levels deep. yaconf.check_delay defaults to 300 seconds and is the interval at which Yaconf checks for changes by comparing directory mtimes, first the configured directory and then each tracked sub-directory.

The build badges at the top of the readme show Linux and Windows CI workflows, and there is an older AppVeyor badge for the master branch.

Two static methods and a C header

All Yaconf methods are static, so you call them on the class directly rather than on an instance, and there are only two of them.

php
static mixed Yaconf::get(string $name, mixed $default = null)

The name uses dot notation to traverse nested keys, and the readme gives examples like "foo.name", "foo.features.1" and "sub.x.role", with a maximum nesting depth of 64. It returns the configuration value on success, or the default, which defaults to null, if the key is not found.

The second is the existence check.

php
static bool Yaconf::has(string $name)
php
<?php
var_dump(Yaconf::has("foo.name"));      // bool(true)
var_dump(Yaconf::has("foo.not_exist")); // bool(false)

What is interesting is the third interface, which is not for PHP code at all. Yaconf exports two functions via php_yaconf.h for use by other PHP extensions, and they mirror the two methods in C.

c
PHP_YACONF_API zval *php_yaconf_get(zend_string *name);
PHP_YACONF_API int    php_yaconf_has(zend_string *name);

The header is installed by make install, and an extension that wants it includes it with the ext/yaconf/php_yaconf.h path. So another extension can read the same configuration without going through the userland API and without parsing the files a second time, which is the point of exporting it at all.

There is also a constant, YACONF_HAVE_YAML, which is always registered and tells you whether YAML support was compiled into the build, so a program can check rather than guess whether the .yaml files were read.

Sections, inheritance and sub-directories

The INI side of the format has two structural features that are easy to miss in the feature list.

INI sections and section inheritance are supported, up to 16 levels deep. That is the traditional INI idea of a base section that other sections extend, and it is the mechanism for keeping a shared set of values in one place.

Sub-directories of arbitrary depth are supported, up to 16 levels, and a file at sub/x.ini is addressed as "sub.x". That arrived in 1.2.0, and it changes the shape of a configuration tree considerably, because the directory layout becomes the namespace. A directory per environment, with the same file names in each, is a natural fit for that, and the key names in PHP stay short because the directory supplies the prefix.

Automatic reloading covers the sub-directories as well, on non-ZTS builds, and the check works by comparing directory mtimes, first the configured directory and then each tracked sub-directory.

The tree in the repository is small, which fits an extension of this size. There is yaconf.c for the implementation, config.m4 and config.w32 for the build system on Unix and Windows, package.xml for the PECL package, yaconf.stub.php and the arginfo headers for the class entry points, php_yaconf.h for the exported C API, and a tests/ directory. There is also a composer.json, a CREDITS file and a LICENSE, and the licence is recorded as a custom one that GitHub cannot classify, so the terms are in the file rather than in a badge.

Yaconf for static configuration, Yac for everything else

The readme draws a line that is worth repeating, because it is the difference between using this extension well and using it badly.

Yaconf stores static configuration, values read often but changed rarely, like credentials, feature flags and routing tables. For runtime caching, things like query results, computed data, HTML fragments and ephemeral tokens, use Yac, which shares the same local first, zero dependency philosophy.

The reasoning is about the lifecycle. Yaconf is loaded once at startup and reloaded rarely, which makes it a good fit for a value that does not change during the life of a request and for values you want resolved once, with INI constants and environment variables expanded at parse time rather than on every access. It is a poor fit for anything per-user or per-request, because the data is global to the process and shared by every worker.

Putting a per-user value in Yaconf is a security bug, not a performance one, because every request in every worker would see the same value. Yac is the sibling that is built for the other half of the problem.

The releases are recent and the version line is short. 1.2.0 came out on 2026-08-18 and 1.1.3 on 2026-08-06, and 1.2.0 is the release that brought the compacted memory layout, the sub-directory support, the PIE install path, so it is the version to read the readme of if you are starting a new deployment. The last push was on 2026-09-23.

Editorial conclusion

Adopt Yaconf when a PHP application parses the same configuration on every request and you want that cost removed, especially when the configuration holds credentials you would rather keep in a directory the web user cannot read. Do not adopt it for runtime caching, because the readme points at Yac for query results and ephemeral tokens, and do not adopt it if you depend on live configuration reloads on ZTS, since ZTS builds never reload. Verify first that your PHP version is 7 or newer, that your configuration fits the section inheritance and sub-directory depth limits of 16 levels, and that you understand the reload semantics of yaconf.check_delay, where zero means checking on every request rather than switching reloading off.

Frequently asked questions

What is Yaconf?

It is a configuration container, a PHP extension written in C, that parses INI files by default with optional YAML support backed by libyaml and stores the result in persistent memory at startup for the entire PHP lifecycle. Yaconf::get() is a pure hash lookup with no file I/O or per-request allocation.

How do I install Yaconf?

Run pecl install yaconf, or since 1.2.0 use PIE with pie install laruence/yaconf. You can also compile from source with phpize, ./configure with --with-php-config, and make, adding --with-yaml to link against the system libyaml.

How does Yaconf keep configuration files out of reach of the web user?

The configuration directory is read once at startup, in the PHP-FPM master process, which normally runs as root, and the workers drop to the pool user after forking. The files can therefore live in a root-owned directory outside the web root, and workers serve from memory without ever reopening them.

What does yaconf.check_delay do?

It is the interval in seconds between checks for configuration changes, compared by directory mtime, and it defaults to 300. Setting it to 0 means checking on every request, which is the most eager setting rather than a way to disable reloading, and the setting is only available in non-ZTS builds because ZTS builds never reload.

What is the difference between Yaconf and Yac?

Yaconf is for static configuration that is read often and changed rarely, such as credentials, feature flags and routing tables. The readme points at Yac for runtime caching of query results, computed data, HTML fragments and ephemeral tokens.

Can another PHP extension read the Yaconf configuration?

Yes. Yaconf exports two functions via php_yaconf.h, php_yaconf_get and php_yaconf_has, which mirror Yaconf::get() and Yaconf::has() in C. The header is installed by make install and is included as ext/yaconf/php_yaconf.h.

Official sources

  1. Issues
  2. laruence/yaconf on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/laruence-yaconf.svg)](https://hysenlabs.com/projects/laruence-yaconf)

Was this useful?