xref: /dokuwiki/lib/plugins/config/core/Configuration.php (revision d868eb89f182718a31113373a6272670bd7f8012)
1c6639e6aSAndreas Gohr<?php
2c6639e6aSAndreas Gohr
3c6639e6aSAndreas Gohrnamespace dokuwiki\plugin\config\core;
47a0ee538SAndreas Gohr
50a5b05ebSAndreas Gohruse dokuwiki\plugin\config\core\Setting\Setting;
67a0ee538SAndreas Gohruse dokuwiki\plugin\config\core\Setting\SettingNoClass;
77a0ee538SAndreas Gohruse dokuwiki\plugin\config\core\Setting\SettingNoDefault;
87a0ee538SAndreas Gohruse dokuwiki\plugin\config\core\Setting\SettingNoKnownClass;
90a5b05ebSAndreas Gohruse dokuwiki\plugin\config\core\Setting\SettingUndefined;
10c6639e6aSAndreas Gohr
11c6639e6aSAndreas Gohr/**
125675a07cSAndreas Gohr * Holds all the current settings and proxies the Loader and Writer
13077c27b2SAndreas Gohr *
14077c27b2SAndreas Gohr * @author Chris Smith <chris@jalakai.co.uk>
15077c27b2SAndreas Gohr * @author Ben Coburn <btcoburn@silicodon.net>
16077c27b2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
17c6639e6aSAndreas Gohr */
188c7c53b0SAndreas Gohrclass Configuration
198c7c53b0SAndreas Gohr{
20c6639e6aSAndreas Gohr
21bf9be0e3SAndreas Gohr    public const KEYMARKER = '____';
22c6639e6aSAndreas Gohr
23077c27b2SAndreas Gohr    /** @var Setting[] metadata as array of Settings objects */
24467c1427SAndreas Gohr    protected $settings = [];
257a0ee538SAndreas Gohr    /** @var Setting[] undefined and problematic settings */
26467c1427SAndreas Gohr    protected $undefined = [];
278ea5685fSAndreas Gohr
28077c27b2SAndreas Gohr    /** @var array all metadata */
29077c27b2SAndreas Gohr    protected $metadata;
30077c27b2SAndreas Gohr    /** @var array all default settings */
31077c27b2SAndreas Gohr    protected $default;
32077c27b2SAndreas Gohr    /** @var array all local settings */
33077c27b2SAndreas Gohr    protected $local;
34077c27b2SAndreas Gohr    /** @var array all protected settings */
35077c27b2SAndreas Gohr    protected $protected;
36077c27b2SAndreas Gohr
37077c27b2SAndreas Gohr    /** @var bool have the settings been changed since loading from disk? */
38077c27b2SAndreas Gohr    protected $changed = false;
39077c27b2SAndreas Gohr
405675a07cSAndreas Gohr    /** @var Loader */
415675a07cSAndreas Gohr    protected $loader;
42077c27b2SAndreas Gohr    /** @var Writer */
43077c27b2SAndreas Gohr    protected $writer;
448ea5685fSAndreas Gohr
45c6639e6aSAndreas Gohr    /**
46077c27b2SAndreas Gohr     * ConfigSettings constructor.
47c6639e6aSAndreas Gohr     */
48*d868eb89SAndreas Gohr    public function __construct()
49*d868eb89SAndreas Gohr    {
505675a07cSAndreas Gohr        $this->loader = new Loader(new ConfigParser());
51077c27b2SAndreas Gohr        $this->writer = new Writer();
52077c27b2SAndreas Gohr
535675a07cSAndreas Gohr        $this->metadata = $this->loader->loadMeta();
545675a07cSAndreas Gohr        $this->default = $this->loader->loadDefaults();
555675a07cSAndreas Gohr        $this->local = $this->loader->loadLocal();
565675a07cSAndreas Gohr        $this->protected = $this->loader->loadProtected();
57077c27b2SAndreas Gohr
58077c27b2SAndreas Gohr        $this->initSettings();
59c6639e6aSAndreas Gohr    }
60c6639e6aSAndreas Gohr
61c6639e6aSAndreas Gohr    /**
62077c27b2SAndreas Gohr     * Get all settings
63c6639e6aSAndreas Gohr     *
64077c27b2SAndreas Gohr     * @return Setting[]
65c6639e6aSAndreas Gohr     */
66*d868eb89SAndreas Gohr    public function getSettings()
67*d868eb89SAndreas Gohr    {
68077c27b2SAndreas Gohr        return $this->settings;
69c6639e6aSAndreas Gohr    }
70c6639e6aSAndreas Gohr
71077c27b2SAndreas Gohr    /**
727a0ee538SAndreas Gohr     * Get all unknown or problematic settings
73077c27b2SAndreas Gohr     *
74077c27b2SAndreas Gohr     * @return Setting[]
75077c27b2SAndreas Gohr     */
76*d868eb89SAndreas Gohr    public function getUndefined()
77*d868eb89SAndreas Gohr    {
78077c27b2SAndreas Gohr        return $this->undefined;
79c6639e6aSAndreas Gohr    }
80c6639e6aSAndreas Gohr
81077c27b2SAndreas Gohr    /**
82077c27b2SAndreas Gohr     * Have the settings been changed since loading from disk?
83077c27b2SAndreas Gohr     *
84077c27b2SAndreas Gohr     * @return bool
85077c27b2SAndreas Gohr     */
86*d868eb89SAndreas Gohr    public function hasChanged()
87*d868eb89SAndreas Gohr    {
88077c27b2SAndreas Gohr        return $this->changed;
89077c27b2SAndreas Gohr    }
90077c27b2SAndreas Gohr
91077c27b2SAndreas Gohr    /**
92077c27b2SAndreas Gohr     * Check if the config can be written
93077c27b2SAndreas Gohr     *
94077c27b2SAndreas Gohr     * @return bool
95077c27b2SAndreas Gohr     */
96*d868eb89SAndreas Gohr    public function isLocked()
97*d868eb89SAndreas Gohr    {
98077c27b2SAndreas Gohr        return $this->writer->isLocked();
99077c27b2SAndreas Gohr    }
100077c27b2SAndreas Gohr
101077c27b2SAndreas Gohr    /**
102077c27b2SAndreas Gohr     * Update the settings using the data provided
103077c27b2SAndreas Gohr     *
104077c27b2SAndreas Gohr     * @param array $input as posted
105077c27b2SAndreas Gohr     * @return bool true if all updates went through, false on errors
106077c27b2SAndreas Gohr     */
107*d868eb89SAndreas Gohr    public function updateSettings($input)
108*d868eb89SAndreas Gohr    {
109077c27b2SAndreas Gohr        $ok = true;
110077c27b2SAndreas Gohr
111077c27b2SAndreas Gohr        foreach($this->settings as $key => $obj) {
112467c1427SAndreas Gohr            $value = $input[$key] ?? null;
113077c27b2SAndreas Gohr            if($obj->update($value)) {
114077c27b2SAndreas Gohr                $this->changed = true;
115077c27b2SAndreas Gohr            }
1165c17d2d3SAndreas Gohr            if($obj->hasError()) $ok = false;
117077c27b2SAndreas Gohr        }
118077c27b2SAndreas Gohr
119077c27b2SAndreas Gohr        return $ok;
120077c27b2SAndreas Gohr    }
121077c27b2SAndreas Gohr
122077c27b2SAndreas Gohr    /**
123077c27b2SAndreas Gohr     * Save the settings
124077c27b2SAndreas Gohr     *
12553f3816eSAndreas Gohr     * This save the current state as defined in this object, including the
12653f3816eSAndreas Gohr     * undefined settings
12753f3816eSAndreas Gohr     *
128077c27b2SAndreas Gohr     * @throws \Exception
129077c27b2SAndreas Gohr     */
130*d868eb89SAndreas Gohr    public function save()
131*d868eb89SAndreas Gohr    {
1327a0ee538SAndreas Gohr        // only save the undefined settings that have not been handled in settings
1337a0ee538SAndreas Gohr        $undefined = array_diff_key($this->undefined, $this->settings);
1347a0ee538SAndreas Gohr        $this->writer->save(array_merge($this->settings, $undefined));
135077c27b2SAndreas Gohr    }
136077c27b2SAndreas Gohr
137077c27b2SAndreas Gohr    /**
138077c27b2SAndreas Gohr     * Touch the settings
139077c27b2SAndreas Gohr     *
140077c27b2SAndreas Gohr     * @throws \Exception
141077c27b2SAndreas Gohr     */
142*d868eb89SAndreas Gohr    public function touch()
143*d868eb89SAndreas Gohr    {
144077c27b2SAndreas Gohr        $this->writer->touch();
145077c27b2SAndreas Gohr    }
146077c27b2SAndreas Gohr
147077c27b2SAndreas Gohr    /**
1485675a07cSAndreas Gohr     * Load the extension language strings
1495675a07cSAndreas Gohr     *
1505675a07cSAndreas Gohr     * @return array
1515675a07cSAndreas Gohr     */
152*d868eb89SAndreas Gohr    public function getLangs()
153*d868eb89SAndreas Gohr    {
1545675a07cSAndreas Gohr        return $this->loader->loadLangs();
1555675a07cSAndreas Gohr    }
1565675a07cSAndreas Gohr
1575675a07cSAndreas Gohr    /**
158077c27b2SAndreas Gohr     * Initalizes the $settings and $undefined properties
159077c27b2SAndreas Gohr     */
160*d868eb89SAndreas Gohr    protected function initSettings()
161*d868eb89SAndreas Gohr    {
162467c1427SAndreas Gohr        $keys = [
163467c1427SAndreas Gohr            ...array_keys($this->metadata),
164467c1427SAndreas Gohr            ...array_keys($this->default),
165467c1427SAndreas Gohr            ...array_keys($this->local),
166467c1427SAndreas Gohr            ...array_keys($this->protected)
167467c1427SAndreas Gohr        ];
168077c27b2SAndreas Gohr        $keys = array_unique($keys);
169077c27b2SAndreas Gohr
170077c27b2SAndreas Gohr        foreach($keys as $key) {
171077c27b2SAndreas Gohr            $obj = $this->instantiateClass($key);
172077c27b2SAndreas Gohr
173077c27b2SAndreas Gohr            if($obj->shouldHaveDefault() && !isset($this->default[$key])) {
1747a0ee538SAndreas Gohr                $this->undefined[$key] = new SettingNoDefault($key);
175077c27b2SAndreas Gohr            }
176077c27b2SAndreas Gohr
177467c1427SAndreas Gohr            $d = $this->default[$key] ?? null;
178467c1427SAndreas Gohr            $l = $this->local[$key] ?? null;
179467c1427SAndreas Gohr            $p = $this->protected[$key] ?? null;
180077c27b2SAndreas Gohr
181077c27b2SAndreas Gohr            $obj->initialize($d, $l, $p);
182077c27b2SAndreas Gohr        }
183077c27b2SAndreas Gohr    }
184077c27b2SAndreas Gohr
185077c27b2SAndreas Gohr    /**
186077c27b2SAndreas Gohr     * Instantiates the proper class for the given config key
187077c27b2SAndreas Gohr     *
188077c27b2SAndreas Gohr     * The class is added to the $settings or $undefined arrays and returned
189077c27b2SAndreas Gohr     *
190077c27b2SAndreas Gohr     * @param string $key
191077c27b2SAndreas Gohr     * @return Setting
192077c27b2SAndreas Gohr     */
193*d868eb89SAndreas Gohr    protected function instantiateClass($key)
194*d868eb89SAndreas Gohr    {
195077c27b2SAndreas Gohr        if(isset($this->metadata[$key])) {
196077c27b2SAndreas Gohr            $param = $this->metadata[$key];
197077c27b2SAndreas Gohr            $class = $this->determineClassName(array_shift($param), $key); // first param is class
198077c27b2SAndreas Gohr            $obj = new $class($key, $param);
199d6987bddSAndreas Gohr            $this->settings[$key] = $obj;
200077c27b2SAndreas Gohr        } else {
201077c27b2SAndreas Gohr            $obj = new SettingUndefined($key);
202d6987bddSAndreas Gohr            $this->undefined[$key] = $obj;
203077c27b2SAndreas Gohr        }
204077c27b2SAndreas Gohr        return $obj;
205077c27b2SAndreas Gohr    }
206077c27b2SAndreas Gohr
207077c27b2SAndreas Gohr    /**
208077c27b2SAndreas Gohr     * Return the class to load
209077c27b2SAndreas Gohr     *
210077c27b2SAndreas Gohr     * @param string $class the class name as given in the meta file
211077c27b2SAndreas Gohr     * @param string $key the settings key
212077c27b2SAndreas Gohr     * @return string
213077c27b2SAndreas Gohr     */
214*d868eb89SAndreas Gohr    protected function determineClassName($class, $key)
215*d868eb89SAndreas Gohr    {
216077c27b2SAndreas Gohr        // try namespaced class first
217b71f2463SAndreas Gohr        if(is_string($class)) {
218077c27b2SAndreas Gohr            $modern = str_replace('_', '', ucwords($class, '_'));
2190a5b05ebSAndreas Gohr            $modern = '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting' . $modern;
220077c27b2SAndreas Gohr            if($modern && class_exists($modern)) return $modern;
2210a5b05ebSAndreas Gohr            // try class as given
2220a5b05ebSAndreas Gohr            if(class_exists($class)) return $class;
223077c27b2SAndreas Gohr            // class wasn't found add to errors
2247a0ee538SAndreas Gohr            $this->undefined[$key] = new SettingNoKnownClass($key);
225077c27b2SAndreas Gohr        } else {
226077c27b2SAndreas Gohr            // no class given, add to errors
2277a0ee538SAndreas Gohr            $this->undefined[$key] = new SettingNoClass($key);
228077c27b2SAndreas Gohr        }
2290a5b05ebSAndreas Gohr        return '\\dokuwiki\\plugin\\config\\core\\Setting\\Setting';
230077c27b2SAndreas Gohr    }
231077c27b2SAndreas Gohr}
232