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