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