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