Reading and Writing Configuration
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
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;
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.
[$key => $merged], or false when the values are empty or the file does not exist.
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.
<?php
return [
'cache' => 'on',
'rich-url' => 'on',
];
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
public static function getd($name = '');
public static function setd($name = '', $content = '');
public static function deld($name = '');
0, [] and {}. A row that does not exist answers null.
null.
Choosing a Store
| Value | Store | Why |
|---|---|---|
| A switch an operator sets once | File | Read on almost every request; a file read is cheaper than a query |
| Credentials for a service | File, in the module's own file | Travels with the module and is not scattered across tables |
| State the application updates itself | Database | Written often, and a file write is not the right tool for that |
| Something one operator prefers | Neither | Per-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.
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]);
}
$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
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.
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.
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.
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.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.