]> git.mxchange.org Git - friendica.git/blob - src/Core/Config/ValueObject/Cache.php
Disable setting fields in case we use environment variables
[friendica.git] / src / Core / Config / ValueObject / Cache.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Core\Config\ValueObject;
23
24 use Friendica\Core\Config\Util\ConfigFileManager;
25 use ParagonIE\HiddenString\HiddenString;
26
27 /**
28  * The Friendica config cache for the application
29  * Initial, all *.config.php files are loaded into this cache with the
30  * ConfigFileManager ( @see ConfigFileManager )
31  */
32 class Cache
33 {
34         /** @var int[] A list of valid config source  */
35         const VALID_SOURCES = [
36                 self::SOURCE_STATIC,
37                 self::SOURCE_FILE,
38                 self::SOURCE_DATA,
39                 self::SOURCE_ENV,
40                 self::SOURCE_FIX,
41         ];
42
43         /** @var int Indicates that the cache entry is a default value - Lowest Priority */
44         const SOURCE_STATIC = 0;
45         /** @var int Indicates that the cache entry is set by file - Low Priority */
46         const SOURCE_FILE = 1;
47         /** @var int Indicates that the cache entry is manually set by the application (per admin page/console) - Middle Priority */
48         const SOURCE_DATA = 2;
49         /** @var int Indicates that the cache entry is set by a server environment variable - High Priority */
50         const SOURCE_ENV = 3;
51         /** @var int Indicates that the cache entry is fixed and must not be changed */
52         const SOURCE_FIX = 5;
53
54         /** @var int Default value for a config source */
55         const SOURCE_DEFAULT = self::SOURCE_FILE;
56
57         /**
58          * @var array
59          */
60         private $config = [];
61
62         /**
63          * @var int[][]
64          */
65         private $source = [];
66
67         /**
68          * @var bool[][]
69          */
70         private $delConfig = [];
71
72         /**
73          * @var bool
74          */
75         private $hidePasswordOutput;
76
77         /**
78          * @param array $config             A initial config array
79          * @param bool  $hidePasswordOutput True, if cache variables should take extra care of password values
80          * @param int   $source             Sets a source of the initial config values
81          */
82         public function __construct(array $config = [], bool $hidePasswordOutput = true, int $source = self::SOURCE_DEFAULT)
83         {
84                 $this->hidePasswordOutput = $hidePasswordOutput;
85                 $this->load($config, $source);
86         }
87
88         /**
89          * Tries to load the specified configuration array into the config array.
90          * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
91          *
92          * @param array $config
93          * @param int   $source Indicates the source of the config entry
94          */
95         public function load(array $config, int $source = self::SOURCE_DEFAULT)
96         {
97                 $categories = array_keys($config);
98
99                 foreach ($categories as $category) {
100                         if (is_array($config[$category])) {
101                                 $keys = array_keys($config[$category]);
102
103                                 foreach ($keys as $key) {
104                                         $value = $config[$category][$key];
105                                         if (isset($value)) {
106                                                 $this->set($category, $key, $value, $source);
107                                         }
108                                 }
109                         }
110                 }
111         }
112
113         /**
114          * Gets a value from the config cache.
115          *
116          * @param string      $cat Config category
117          * @param string|null $key Config key
118          *
119          * @return null|mixed Returns the value of the Config entry or null if not set
120          */
121         public function get(string $cat, ?string $key = null)
122         {
123                 if (isset($this->config[$cat][$key])) {
124                         return $this->config[$cat][$key];
125                 } elseif (!isset($key) && isset($this->config[$cat])) {
126                         return $this->config[$cat];
127                 } else {
128                         return null;
129                 }
130         }
131
132         /**
133          * Returns the source value of the current, cached config value
134          *
135          * @param string $cat Config category
136          * @param string $key Config key
137          *
138          * @return int
139          */
140         public function getSource(string $cat, string $key): int
141         {
142                 return $this->source[$cat][$key] ?? -1;
143         }
144
145         /**
146          * Returns the whole config array based on the given source type
147          *
148          * @param int $source Indicates the source of the config entry
149          *
150          * @return array The config array part of the given source
151          */
152         public function getDataBySource(int $source): array
153         {
154                 $data = [];
155
156                 $categories = array_keys($this->source);
157
158                 foreach ($categories as $category) {
159                         if (is_array($this->source[$category])) {
160                                 $keys = array_keys($this->source[$category]);
161
162                                 foreach ($keys as $key) {
163                                         if ($this->source[$category][$key] === $source) {
164                                                 $data[$category][$key] = $this->config[$category][$key];
165                                         }
166                                 }
167                         }
168                 }
169
170                 return $data;
171         }
172
173         /**
174          * Sets a value in the config cache. Accepts raw output from the config table
175          *
176          * @param string $cat    Config category
177          * @param string $key    Config key
178          * @param mixed  $value  Value to set
179          * @param int    $source The source of the current config key
180          *
181          * @return bool True, if the value is set
182          */
183         public function set(string $cat, string $key, $value, int $source = self::SOURCE_DEFAULT): bool
184         {
185                 if (!isset($this->config[$cat])) {
186                         $this->config[$cat] = [];
187                         $this->source[$cat] = [];
188                 }
189
190                 if (isset($this->source[$cat][$key]) &&
191                         $source < $this->source[$cat][$key]) {
192                         return false;
193                 }
194
195                 if ($this->hidePasswordOutput &&
196                         $key == 'password' &&
197                         is_string($value)) {
198                         $this->config[$cat][$key] = new HiddenString((string)$value);
199                 } else if (is_string($value)) {
200                         $this->config[$cat][$key] = self::toConfigValue($value);
201                 } else {
202                         $this->config[$cat][$key] = $value;
203                 }
204
205                 $this->source[$cat][$key] = $source;
206
207                 return true;
208         }
209
210         /**
211          * Formats a DB value to a config value
212          * - null   = The db-value isn't set
213          * - bool   = The db-value is either '0' or '1'
214          * - array  = The db-value is a serialized array
215          * - string = The db-value is a string
216          *
217          * Keep in mind that there aren't any numeric/integer config values in the database
218          *
219          * @param string|null $value
220          *
221          * @return mixed
222          */
223         public static function toConfigValue(?string $value)
224         {
225                 if (!isset($value)) {
226                         return null;
227                 }
228
229                 if (preg_match("|^a:[0-9]+:{.*}$|s", $value)) {
230                         return unserialize($value);
231                 } else {
232                         return $value;
233                 }
234         }
235
236         /**
237          * Deletes a value from the config cache.
238          *
239          * @param string $cat Config category
240          * @param string $key Config key
241          *
242          * @return bool true, if deleted
243          */
244         public function delete(string $cat, string $key): bool
245         {
246                 if (isset($this->config[$cat][$key])) {
247                         unset($this->config[$cat][$key]);
248                         unset($this->source[$cat][$key]);
249                         $this->delConfig[$cat][$key] = true;
250                         if (count($this->config[$cat]) == 0) {
251                                 unset($this->config[$cat]);
252                                 unset($this->source[$cat]);
253                         }
254                         return true;
255                 } else {
256                         return false;
257                 }
258         }
259
260         /**
261          * Returns the whole configuration
262          *
263          * @return string[][] The configuration
264          */
265         public function getAll(): array
266         {
267                 return $this->config;
268         }
269
270         /**
271          * Returns an array with missing categories/Keys
272          *
273          * @param string[][] $config The array to check
274          *
275          * @return string[][]
276          */
277         public function keyDiff(array $config): array
278         {
279                 $return = [];
280
281                 $categories = array_keys($config);
282
283                 foreach ($categories as $category) {
284                         if (is_array($config[$category])) {
285                                 $keys = array_keys($config[$category]);
286
287                                 foreach ($keys as $key) {
288                                         if (!isset($this->config[$category][$key])) {
289                                                 $return[$category][$key] = $config[$category][$key];
290                                         }
291                                 }
292                         }
293                 }
294
295                 return $return;
296         }
297
298         /**
299          * Merges a new Cache into the existing one and returns the merged Cache
300          *
301          * @param Cache $cache The cache, which should get merged into this Cache
302          *
303          * @return Cache The merged Cache
304          */
305         public function merge(Cache $cache): Cache
306         {
307                 $newConfig = $this->config;
308                 $newSource = $this->source;
309
310                 $categories = array_keys($cache->config);
311
312                 foreach ($categories as $category) {
313                         if (is_array($cache->config[$category])) {
314                                 $keys = array_keys($cache->config[$category]);
315
316                                 if (is_null($newConfig[$category] ?? null)) {
317                                         $newConfig[$category] = [];
318                                         $newSource[$category] = [];
319                                 }
320
321                                 foreach ($keys as $key) {
322                                         $newConfig[$category][$key] = $cache->config[$category][$key];
323                                         $newSource[$category][$key] = $cache->source[$category][$key];
324                                 }
325                         } else {
326                                 $newConfig[$category] = $cache->config[$category];
327                                 $newSource[$category] = $cache->source[$category];
328                         }
329                 }
330
331                 $delCategories = array_keys($cache->delConfig);
332
333                 foreach ($delCategories as $category) {
334                         if (is_array($cache->delConfig[$category])) {
335                                 $keys = array_keys($cache->delConfig[$category]);
336
337                                 foreach ($keys as $key) {
338                                         unset($newConfig[$category][$key]);
339                                         unset($newSource[$category][$key]);
340                                 }
341                         }
342                 }
343
344                 $newCache = new Cache();
345                 $newCache->config = $newConfig;
346                 $newCache->source = $newSource;
347
348                 return $newCache;
349         }
350 }