Reading and Writing Configuration

5 views Markdown

Settings live in two places with different rules. PHP files are loaded on demand, and database rows hold values that do not belong in a file.

Overview

A configuration file is a PHP file that returns an array. Reading one is a path expression rather than a file operation. You name the file, then walk into the array with slashes. The file is loaded the first time something asks for it.

The second store is a table, for values that are per-installation state rather than configuration a developer ships. Both are reached through the same class with different method names, and choosing the wrong one is the mistake worth avoiding.

Reference

File-Based Settings

Config, files
public static function get($arg = null);
public static function set($key, $values, $merge = false): array|false;
public static function save($name = '', $data = []): bool;
public static function merge($arg = []): bool;
get($arg) The first path segment is the file, the rest walks into the array it returns. Any segment that is missing answers false, so a stored false and an unset key look the same. With no argument you get every file loaded so far, which is not every file that exists.
set($key, $values, $merge) Changes the loaded copy for this request and writes nothing. First argument is the file name, second the values. Answers [$key => $merged], or false when the values are empty or the file does not exist.
save($name, $data) Writes the array back to its file and refreshes the loaded copy. A later read in the same request sees the new value. With no second argument it saves whatever is currently in memory for that file. Answers whether the write succeeded.
merge($arg) Folds whole entries into the loaded set for this request, so its keys are file names, not settings. Useful for a virtual file that has no counterpart on disk.
coremio/configuration Where the files live, one per name: general, options, theme, crypt, sms and the rest.

What save Expects and Writes

A file is a returned array and may be wrapped in its own name or not. Both shapes are accepted. If the file on disk is wrapped and your data is not, it is wrapped for you before writing.

the file on disk, and the two shapes that produce it
<?php
return [
    'cache'    => 'on',
    'rich-url' => 'on',
];
both calls are correct
Config::save("general", ['cache' => 'on', 'rich-url' => 'on']);
Config::save("general", ['general' => ['cache' => 'on', 'rich-url' => 'on']]);

The source is generated by the array exporter and written through the file manager. That manager invalidates the compiled copy of the file it replaced. A raw write has to do that itself.

Database-Backed Settings

Config, rows
public static function getd($name = '');
public static function setd($name = '', $content = '');
public static function deld($name = '');
getd($name) Reads one named row. A stored value that is valid JSON comes back decoded. An array is an array again, and a stored number is a number. That holds only while the decoded value is truthy. A stored zero, an empty array and an empty object come back as the written text: 0, [] and {}. A row that does not exist answers null.
setd($name, $content) Inserts or updates by name. An array is encoded for you; anything else is stored as given. A new row is stamped with a created and an updated time; an update touches the updated one only.
deld($name) Removes the row. There is no soft delete, and a later read answers null.

Choosing a Store

ValueStoreWhy
A switch an operator sets onceFileRead on almost every request; a file read is cheaper than a query
Credentials for a serviceFile, in the module's own fileTravels with the module and is not scattered across tables
State the application updates itselfDatabaseWritten often, and a file write is not the right tool for that
Something one operator prefersNeitherPer-person preference belongs to that person, not to the installation

Example

Read the file, change the value, write it, and read it back in the same request.

a settings operation
public function save_general(Operation $operation): bool
{
    $operation->demo();

    $data          = Config::get("general");                  // the whole file
    $data['cache'] = Filter::init("POST/cache", "route") === 'on' ? 'on' : 'off';

    if (!Config::save("general", $data))
        throw new Exception(Language::gc("error/settings-not-written"));

    // Same request, and the value is already the new one.
    $now = Config::get("general/cache");

    // State the application owns goes to the row store instead.
    Config::setd("last_settings_change", ['at' => date('c'), 'cache' => $now]);

    return $operation->output(['status' => "successful", 'cache' => $now]);
}
reading the row back somewhere else
$state = Config::getd("last_settings_change");   // array, decoded for you

if ($state === null) {
    // Never written, or removed. Not the same as "written as empty".
}

Pitfalls

The third argument of set does the opposite of its name

Left at its default it replaces recursively, so siblings inside a nested key survive. Passing true merges at the top level only, which replaces a nested array whole and drops the keys you did not mention. Changing one key under a group is the default behaviour, not the merging one.

Changing a value in memory does not persist it

The setter and the saver are separate on purpose, so a request can override a setting for itself without rewriting the file. If a change has to survive the request, save it. Saving with no data argument commits whatever the setter left in memory.

A configuration file is compiled PHP

The server can keep a compiled copy and go on serving it after the file changes. A save then looks successful, and the old value comes back on the next page. Write through the file manager, which invalidates that copy; a raw write has to do it itself.

Configuration is installation-wide

There is one value for everyone. Storing a personal preference here means the first operator who changes it changes it for every other operator too. That kind of value belongs in a cookie or on the user record.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.