]> git.mxchange.org Git - friendica.git/blob - src/Util/ConfigFileLoader.php
Add the possibility to use a different configuration directory
[friendica.git] / src / Util / ConfigFileLoader.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, 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\Util;
23
24 use Exception;
25 use Friendica\Core\Addon;
26 use Friendica\Core\Config\Cache;
27
28 /**
29  * The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache )
30  *
31  * It is capable of loading the following config files:
32  * - *.config.php   (current)
33  * - *.ini.php      (deprecated)
34  * - *.htconfig.php (deprecated)
35  */
36 class ConfigFileLoader
37 {
38         /**
39          * The key of the $_SERVER variable to override the config directory
40          *
41          * @var string
42          */
43         const CONFIG_DIR_ENV = 'FRIENDICA_CONFIG_DIR';
44
45         /**
46          * The Sub directory of the config-files
47          *
48          * @var string
49          */
50         const CONFIG_DIR = 'config';
51
52         /**
53          * The Sub directory of the static config-files
54          *
55          * @var string
56          */
57         const STATIC_DIR = 'static';
58
59         /**
60          * The default name of the user defined ini file
61          *
62          * @var string
63          */
64         const CONFIG_INI = 'local';
65
66         /**
67          * The default name of the user defined legacy config file
68          *
69          * @var string
70          */
71         const CONFIG_HTCONFIG = 'htconfig';
72
73         /**
74          * The sample string inside the configs, which shouldn't get loaded
75          *
76          * @var string
77          */
78         const SAMPLE_END = '-sample';
79
80         /**
81          * @var string
82          */
83         private $baseDir;
84         /**
85          * @var string
86          */
87         private $configDir;
88         /**
89          * @var string
90          */
91         private $staticDir;
92
93         public function __construct(string $basePath, array $server)
94         {
95                 $this->baseDir = $basePath;
96                 if (!empty($server[self::CONFIG_DIR_ENV]) && is_dir($server[self::CONFIG_DIR_ENV])) {
97                         $this->configDir = $server[self::CONFIG_DIR_ENV];
98                 } else {
99                         $this->configDir = $this->baseDir . DIRECTORY_SEPARATOR . self::CONFIG_DIR;
100                 }
101                 $this->staticDir = $this->baseDir . DIRECTORY_SEPARATOR . self::STATIC_DIR;
102         }
103
104         /**
105          * Load the configuration files into an configuration cache
106          *
107          * First loads the default value for all the configuration keys, then the legacy configuration files, then the
108          * expected local.config.php
109          *
110          * @param Cache $config The config cache to load to
111          * @param array $server The $_SERVER array
112          * @param bool  $raw    Setup the raw config format
113          *
114          * @throws Exception
115          */
116         public function setupCache(Cache $config, array $server = [], $raw = false)
117         {
118                 // Load static config files first, the order is important
119                 $config->load($this->loadStaticConfig('defaults'), Cache::SOURCE_FILE);
120                 $config->load($this->loadStaticConfig('settings'), Cache::SOURCE_FILE);
121
122                 // try to load the legacy config first
123                 $config->load($this->loadLegacyConfig('htpreconfig'), Cache::SOURCE_FILE);
124                 $config->load($this->loadLegacyConfig('htconfig'), Cache::SOURCE_FILE);
125
126                 // Now load every other config you find inside the 'config/' directory
127                 $this->loadCoreConfig($config);
128
129                 $config->load($this->loadEnvConfig($server), Cache::SOURCE_ENV);
130
131                 // In case of install mode, add the found basepath (because there isn't a basepath set yet
132                 if (!$raw && empty($config->get('system', 'basepath'))) {
133                         // Setting at least the basepath we know
134                         $config->set('system', 'basepath', $this->baseDir, Cache::SOURCE_FILE);
135                 }
136         }
137
138         /**
139          * Tries to load the static core-configuration and returns the config array.
140          *
141          * @param string $name The name of the configuration
142          *
143          * @return array The config array (empty if no config found)
144          *
145          * @throws Exception if the configuration file isn't readable
146          */
147         private function loadStaticConfig($name)
148         {
149                 $configName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.config.php';
150                 $iniName    = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.ini.php';
151
152                 if (file_exists($configName)) {
153                         return $this->loadConfigFile($configName);
154                 } elseif (file_exists($iniName)) {
155                         return $this->loadINIConfigFile($iniName);
156                 } else {
157                         return [];
158                 }
159         }
160
161         /**
162          * Tries to load the specified core-configuration into the config cache.
163          *
164          * @param Cache $config The Config cache
165          *
166          * @return array The config array (empty if no config found)
167          *
168          * @throws Exception if the configuration file isn't readable
169          */
170         private function loadCoreConfig(Cache $config)
171         {
172                 // try to load legacy ini-files first
173                 foreach ($this->getConfigFiles(true) as $configFile) {
174                         $config->load($this->loadINIConfigFile($configFile), Cache::SOURCE_FILE);
175                 }
176
177                 // try to load supported config at last to overwrite it
178                 foreach ($this->getConfigFiles() as $configFile) {
179                         $config->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE);
180                 }
181
182                 return [];
183         }
184
185         /**
186          * Tries to load the specified addon-configuration and returns the config array.
187          *
188          * @param string $name The name of the configuration
189          *
190          * @return array The config array (empty if no config found)
191          *
192          * @throws Exception if the configuration file isn't readable
193          */
194         public function loadAddonConfig($name)
195         {
196                 $filepath = $this->baseDir . DIRECTORY_SEPARATOR .   // /var/www/html/
197                             Addon::DIRECTORY . DIRECTORY_SEPARATOR . // addon/
198                             $name . DIRECTORY_SEPARATOR .            // openstreetmap/
199                             self::CONFIG_DIR . DIRECTORY_SEPARATOR . // config/
200                             $name . ".config.php";                   // openstreetmap.config.php
201
202                 if (file_exists($filepath)) {
203                         return $this->loadConfigFile($filepath);
204                 } else {
205                         return [];
206                 }
207         }
208
209         /**
210          * Tries to load environment specific variables, based on the `env.config.php` mapping table
211          *
212          * @param array $server The $_SERVER variable
213          *
214          * @return array The config array (empty if no config was found)
215          *
216          * @throws Exception if the configuration file isn't readable
217          */
218         public function loadEnvConfig(array $server)
219         {
220                 $filepath = $this->baseDir . DIRECTORY_SEPARATOR .   // /var/www/html/
221                                         self::STATIC_DIR . DIRECTORY_SEPARATOR . // static/
222                                         "env.config.php";                        // env.config.php
223
224                 if (!file_exists($filepath)) {
225                         return [];
226                 }
227
228                 $envConfig = $this->loadConfigFile($filepath);
229
230                 $return = [];
231
232                 foreach ($envConfig as $envKey => $configStructure) {
233                         if (isset($server[$envKey])) {
234                                 $return[$configStructure[0]][$configStructure[1]] = $server[$envKey];
235                         }
236                 }
237
238                 return $return;
239         }
240
241         /**
242          * Get the config files of the config-directory
243          *
244          * @param bool $ini True, if scan for ini-files instead of config files
245          *
246          * @return array
247          */
248         private function getConfigFiles(bool $ini = false)
249         {
250                 $files = scandir($this->configDir);
251                 $found = array();
252
253                 $filePattern = ($ini ? '*.ini.php' : '*.config.php');
254
255                 // Don't load sample files
256                 $sampleEnd = self::SAMPLE_END . ($ini ? '.ini.php' : '.config.php');
257
258                 foreach ($files as $filename) {
259                         if (fnmatch($filePattern, $filename) && substr_compare($filename, $sampleEnd, -strlen($sampleEnd))) {
260                                 $found[] = $this->configDir . '/' . $filename;
261                         }
262                 }
263
264                 return $found;
265         }
266
267         /**
268          * Tries to load the legacy config files (.htconfig.php, .htpreconfig.php) and returns the config array.
269          *
270          * @param string $name The name of the config file (default is empty, which means .htconfig.php)
271          *
272          * @return array The configuration array (empty if no config found)
273          *
274          * @deprecated since version 2018.09
275          */
276         private function loadLegacyConfig($name = '')
277         {
278                 $name     = !empty($name) ? $name : self::CONFIG_HTCONFIG;
279                 $fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php';
280
281                 $config = [];
282                 if (file_exists($fullName)) {
283                         $a         = new \stdClass();
284                         $a->config = [];
285                         include $fullName;
286
287                         $htConfigCategories = array_keys($a->config);
288
289                         // map the legacy configuration structure to the current structure
290                         foreach ($htConfigCategories as $htConfigCategory) {
291                                 if (is_array($a->config[$htConfigCategory])) {
292                                         $keys = array_keys($a->config[$htConfigCategory]);
293
294                                         foreach ($keys as $key) {
295                                                 $config[$htConfigCategory][$key] = $a->config[$htConfigCategory][$key];
296                                         }
297                                 } else {
298                                         $config['config'][$htConfigCategory] = $a->config[$htConfigCategory];
299                                 }
300                         }
301
302                         unset($a);
303
304                         if (isset($db_host)) {
305                                 $config['database']['hostname'] = $db_host;
306                                 unset($db_host);
307                         }
308                         if (isset($db_user)) {
309                                 $config['database']['username'] = $db_user;
310                                 unset($db_user);
311                         }
312                         if (isset($db_pass)) {
313                                 $config['database']['password'] = $db_pass;
314                                 unset($db_pass);
315                         }
316                         if (isset($db_data)) {
317                                 $config['database']['database'] = $db_data;
318                                 unset($db_data);
319                         }
320                         if (isset($config['system']['db_charset'])) {
321                                 $config['database']['charset'] = $config['system']['db_charset'];
322                         }
323                         if (isset($pidfile)) {
324                                 $config['system']['pidfile'] = $pidfile;
325                                 unset($pidfile);
326                         }
327                         if (isset($default_timezone)) {
328                                 $config['system']['default_timezone'] = $default_timezone;
329                                 unset($default_timezone);
330                         }
331                         if (isset($lang)) {
332                                 $config['system']['language'] = $lang;
333                                 unset($lang);
334                         }
335                 }
336
337                 return $config;
338         }
339
340         /**
341          * Tries to load the specified legacy configuration file and returns the config array.
342          *
343          * @param string $filepath
344          *
345          * @return array The configuration array
346          * @throws Exception
347          * @deprecated since version 2018.12
348          */
349         private function loadINIConfigFile($filepath)
350         {
351                 $contents = include($filepath);
352
353                 $config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
354
355                 if ($config === false) {
356                         throw new Exception('Error parsing INI config file ' . $filepath);
357                 }
358
359                 return $config;
360         }
361
362         /**
363          * Tries to load the specified configuration file and returns the config array.
364          *
365          * The config format is PHP array and the template for configuration files is the following:
366          *
367          * <?php return [
368          *      'section' => [
369          *          'key' => 'value',
370          *      ],
371          * ];
372          *
373          * @param string $filepath The filepath of the
374          *
375          * @return array The config array0
376          *
377          * @throws Exception if the config cannot get loaded.
378          */
379         private function loadConfigFile($filepath)
380         {
381                 $config = include($filepath);
382
383                 if (!is_array($config)) {
384                         throw new Exception('Error loading config file ' . $filepath);
385                 }
386
387                 return $config;
388         }
389 }