You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
4.6 KiB
PHP

<?php
namespace CodeIgniter\Settings;
use CodeIgniter\Settings\Config\Settings as SettingsConfig;
use CodeIgniter\Settings\Handlers\BaseHandler;
use InvalidArgumentException;
use RuntimeException;
/**
* Allows developers a single location to store and
* retrieve settings that were original set in config files
* in the core application or any third-party module.
*/
class Settings
{
/**
* An array of handlers for getting/setting the values.
*
* @var BaseHandler[]
*/
private array $handlers = [];
/**
* An array of the config options for each handler.
*
* @var array<string,array<string,mixed>>
*/
private ?array $options = null;
/**
* Grabs instances of our handlers.
*/
public function __construct(SettingsConfig $config)
{
foreach ($config->handlers as $handler) {
$class = $config->{$handler}['class'] ?? null;
if ($class === null) {
continue;
}
$this->handlers[$handler] = new $class();
$this->options[$handler] = $config->{$handler};
}
}
/**
* Retrieve a value from any handler
* or from a config file matching the name
* file.arg.optionalArg
*/
public function get(string $key, ?string $context = null)
{
[$class, $property, $config] = $this->prepareClassAndProperty($key);
// Check each of our handlers
foreach ($this->handlers as $handler) {
if ($handler->has($class, $property, $context)) {
return $handler->get($class, $property, $context);
}
}
// If no contextual value was found then fall back to general
if ($context !== null) {
return $this->get($key);
}
return $config->{$property} ?? null;
}
/**
* Save a value to the writable handler for later retrieval.
*
* @param mixed $value
*
* @return void
*/
public function set(string $key, $value = null, ?string $context = null)
{
[$class, $property] = $this->prepareClassAndProperty($key);
foreach ($this->getWriteHandlers() as $handler) {
$handler->set($class, $property, $value, $context);
}
}
/**
* Removes a setting from the persistent storage,
* effectively returning the value to the default value
* found in the config file, if any.
*
* @return void
*/
public function forget(string $key, ?string $context = null)
{
[$class, $property] = $this->prepareClassAndProperty($key);
foreach ($this->getWriteHandlers() as $handler) {
$handler->forget($class, $property, $context);
}
}
/**
* Removes all settings from the persistent storage,
* Useful during testing. Use with caution.
*
* @return void
*/
public function flush()
{
foreach ($this->getWriteHandlers() as $handler) {
$handler->flush();
}
}
/**
* Returns the handler that is set to store values.
*
* @return BaseHandler[]
*
* @throws RuntimeException
*/
private function getWriteHandlers()
{
$handlers = [];
foreach ($this->options as $handler => $options) {
if (! empty($options['writeable'])) {
$handlers[] = $this->handlers[$handler];
}
}
if ($handlers === []) {
throw new RuntimeException('Unable to find a Settings handler that can store values.');
}
return $handlers;
}
/**
* Analyzes the given key and breaks it into the class.field parts.
*
* @return string[]
*
* @throws InvalidArgumentException
*/
private function parseDotSyntax(string $key): array
{
// Parse the field name for class.field
$parts = explode('.', $key);
if (count($parts) === 1) {
throw new InvalidArgumentException('$key must contain both the class and field name, i.e. Foo.bar');
}
return $parts;
}
/**
* Given a key in class.property syntax, will split the values
* and determine the fully qualified class name, if possible.
*/
private function prepareClassAndProperty(string $key): array
{
[$class, $property] = $this->parseDotSyntax($key);
$config = config($class);
// Use a fully qualified class name if the
// config file was found.
if ($config !== null) {
$class = get_class($config);
}
return [$class, $property, $config];
}
}