* @author Ben Coburn * @author Andreas Gohr */ class Configuration { public const KEYMARKER = '____'; /** @var Setting[] metadata as array of Settings objects */ protected $settings = []; /** @var Setting[] undefined and problematic settings */ protected $undefined = []; /** @var array all metadata */ protected $metadata; /** @var array all default settings */ protected $default; /** @var array all local settings */ protected $local; /** @var array all protected settings */ protected $protected; /** @var bool have the settings been changed since loading from disk? */ protected $changed = false; /** @var Loader */ protected $loader; /** @var Writer */ protected $writer; /** * ConfigSettings constructor. */ public function __construct() { $this->loader = new Loader(new ConfigParser()); $this->writer = new Writer(); $this->metadata = $this->loader->loadMeta(); $this->default = $this->loader->loadDefaults(); $this->local = $this->loader->loadLocal(); $this->protected = $this->loader->loadProtected(); $this->initSettings(); } /** * Get all settings * * @return Setting[] */ public function getSettings() { return $this->settings; } /** * Get all unknown or problematic settings * * @return Setting[] */ public function getUndefined() { return $this->undefined; } /** * Have the settings been changed since loading from disk? * * @return bool */ public function hasChanged() { return $this->changed; } /** * Check if the config can be written * * @return bool */ public function isLocked() { return $this->writer->isLocked(); } /** * Update the settings using the data provided * * @param array $input as posted * @return bool true if all updates went through, false on errors */ public function updateSettings($input) { $ok = true; foreach ($this->settings as $key => $obj) { $value = $input[$key] ?? null; if ($obj->update($value)) { $this->changed = true; } if ($obj->hasError()) $ok = false; } return $ok; } /** * Save the settings * * This save the current state as defined in this object, including the * undefined settings * * @throws \Exception */ public function save() { // only save the undefined settings that have not been handled in settings $undefined = array_diff_key($this->undefined, $this->settings); $this->writer->save(array_merge($this->settings, $undefined)); } /** * Touch the settings * * @throws \Exception */ public function touch() { $this->writer->touch(); } /** * Load the extension language strings * * @return array */ public function getLangs() { return $this->loader->loadLangs(); } /** * Initalizes the $settings and $undefined properties */ protected function initSettings() { $keys = [ ...array_keys($this->metadata), ...array_keys($this->default), ...array_keys($this->local), ...array_keys($this->protected) ]; $keys = array_unique($keys); foreach ($keys as $key) { $obj = $this->instantiateClass($key); if ($obj->shouldHaveDefault() && !isset($this->default[$key])) { $this->undefined[$key] = new SettingNoDefault($key); } $d = $this->default[$key] ?? null; $l = $this->local[$key] ?? null; $p = $this->protected[$key] ?? null; $obj->initialize($d, $l, $p); } } /** * Instantiates the proper class for the given config key * * The class is added to the $settings or $undefined arrays and returned * * @param string $key * @return Setting */ protected function instantiateClass($key) { if (isset($this->metadata[$key])) { $param = $this->metadata[$key]; $class = $this->determineClassName(array_shift($param), $key); // first param is class $obj = new $class($key, $param); $this->settings[$key] = $obj; } else { $obj = new SettingUndefined($key); $this->undefined[$key] = $obj; } return $obj; } /** * Return the class to load * * @param string $class the class name as given in the meta file * @param string $key the settings key * @return string */ protected function determineClassName($class, $key) { // try namespaced class first if (is_string($class)) { $modern = str_replace('_', '', ucwords($class, '_')); $modern = '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting' . $modern; if ($modern && class_exists($modern)) return $modern; // try class as given if (class_exists($class)) return $class; // class wasn't found add to errors $this->undefined[$key] = new SettingNoKnownClass($key); } else { // no class given, add to errors $this->undefined[$key] = new SettingNoClass($key); } return '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting'; } }