xref: /dokuwiki/lib/plugins/config/core/Configuration.php (revision 4667676ba003110a7962f4c0611a71c0d627b581)
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