1<?php 2 3namespace dokuwiki\plugin\config\core; 4 5use dokuwiki\plugin\config\core\Setting\Setting; 6use dokuwiki\plugin\config\core\Setting\SettingNoClass; 7use dokuwiki\plugin\config\core\Setting\SettingNoDefault; 8use dokuwiki\plugin\config\core\Setting\SettingNoKnownClass; 9use dokuwiki\plugin\config\core\Setting\SettingUndefined; 10 11/** 12 * Holds all the current settings and proxies the Loader and Writer 13 * 14 * @author Chris Smith <chris@jalakai.co.uk> 15 * @author Ben Coburn <btcoburn@silicodon.net> 16 * @author Andreas Gohr <andi@splitbrain.org> 17 */ 18class Configuration 19{ 20 public const KEYMARKER = '____'; 21 22 /** @var Setting[] metadata as array of Settings objects */ 23 protected $settings = []; 24 /** @var Setting[] undefined and problematic settings */ 25 protected $undefined = []; 26 27 /** @var array all metadata */ 28 protected $metadata; 29 /** @var array all default settings */ 30 protected $default; 31 /** @var array all local settings */ 32 protected $local; 33 /** @var array all protected settings */ 34 protected $protected; 35 36 /** @var bool have the settings been changed since loading from disk? */ 37 protected $changed = false; 38 39 /** @var Loader */ 40 protected $loader; 41 /** @var Writer */ 42 protected $writer; 43 44 /** 45 * ConfigSettings constructor. 46 */ 47 public function __construct() 48 { 49 $this->loader = new Loader(new ConfigParser()); 50 $this->writer = new Writer(); 51 52 $this->metadata = $this->loader->loadMeta(); 53 $this->default = $this->loader->loadDefaults(); 54 $this->local = $this->loader->loadLocal(); 55 $this->protected = $this->loader->loadProtected(); 56 57 $this->initSettings(); 58 } 59 60 /** 61 * Get all settings 62 * 63 * @return Setting[] 64 */ 65 public function getSettings() 66 { 67 return $this->settings; 68 } 69 70 /** 71 * Get all unknown or problematic settings 72 * 73 * @return Setting[] 74 */ 75 public function getUndefined() 76 { 77 return $this->undefined; 78 } 79 80 /** 81 * Have the settings been changed since loading from disk? 82 * 83 * @return bool 84 */ 85 public function hasChanged() 86 { 87 return $this->changed; 88 } 89 90 /** 91 * Check if the config can be written 92 * 93 * @return bool 94 */ 95 public function isLocked() 96 { 97 return $this->writer->isLocked(); 98 } 99 100 /** 101 * Update the settings using the data provided 102 * 103 * @param array $input as posted 104 * @return bool true if all updates went through, false on errors 105 */ 106 public function updateSettings($input) 107 { 108 $ok = true; 109 110 foreach ($this->settings as $key => $obj) { 111 $value = $input[$key] ?? null; 112 if ($obj->update($value)) { 113 $this->changed = true; 114 } 115 if ($obj->hasError()) $ok = false; 116 } 117 118 return $ok; 119 } 120 121 /** 122 * Save the settings 123 * 124 * This save the current state as defined in this object, including the 125 * undefined settings 126 * 127 * @throws \Exception 128 */ 129 public function save() 130 { 131 // only save the undefined settings that have not been handled in settings 132 $undefined = array_diff_key($this->undefined, $this->settings); 133 $this->writer->save(array_merge($this->settings, $undefined)); 134 } 135 136 /** 137 * Touch the settings 138 * 139 * @throws \Exception 140 */ 141 public function touch() 142 { 143 $this->writer->touch(); 144 } 145 146 /** 147 * Load the extension language strings 148 * 149 * @return array 150 */ 151 public function getLangs() 152 { 153 return $this->loader->loadLangs(); 154 } 155 156 /** 157 * Initalizes the $settings and $undefined properties 158 */ 159 protected function initSettings() 160 { 161 $keys = [ 162 ...array_keys($this->metadata), 163 ...array_keys($this->default), 164 ...array_keys($this->local), 165 ...array_keys($this->protected) 166 ]; 167 $keys = array_unique($keys); 168 169 foreach ($keys as $key) { 170 $obj = $this->instantiateClass($key); 171 172 if ($obj->shouldHaveDefault() && !isset($this->default[$key])) { 173 $this->undefined[$key] = new SettingNoDefault($key); 174 } 175 176 $d = $this->default[$key] ?? null; 177 $l = $this->local[$key] ?? null; 178 $p = $this->protected[$key] ?? null; 179 180 $obj->initialize($d, $l, $p); 181 } 182 } 183 184 /** 185 * Instantiates the proper class for the given config key 186 * 187 * The class is added to the $settings or $undefined arrays and returned 188 * 189 * @param string $key 190 * @return Setting 191 */ 192 protected function instantiateClass($key) 193 { 194 if (isset($this->metadata[$key])) { 195 $param = $this->metadata[$key]; 196 $class = $this->determineClassName(array_shift($param), $key); // first param is class 197 $obj = new $class($key, $param); 198 $this->settings[$key] = $obj; 199 } else { 200 $obj = new SettingUndefined($key); 201 $this->undefined[$key] = $obj; 202 } 203 return $obj; 204 } 205 206 /** 207 * Return the class to load 208 * 209 * @param string $class the class name as given in the meta file 210 * @param string $key the settings key 211 * @return string 212 */ 213 protected function determineClassName($class, $key) 214 { 215 // try namespaced class first 216 if (is_string($class)) { 217 $modern = str_replace('_', '', ucwords($class, '_')); 218 $modern = '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting' . $modern; 219 if ($modern && class_exists($modern)) return $modern; 220 // try class as given 221 if (class_exists($class)) return $class; 222 // class wasn't found add to errors 223 $this->undefined[$key] = new SettingNoKnownClass($key); 224 } else { 225 // no class given, add to errors 226 $this->undefined[$key] = new SettingNoClass($key); 227 } 228 return '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting'; 229 } 230} 231