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