Passed
Push — main ( c0dba5...15ce1f )
by Dimitri
03:26
created

Config::initialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 17
rs 9.9332
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Config;
13
14
use BlitzPHP\Autoloader\Autoloader;
15
use BlitzPHP\Autoloader\Locator;
16
use BlitzPHP\Container\Services;
17
use BlitzPHP\Exceptions\ConfigException;
18
use BlitzPHP\Utilities\Helpers;
19
use BlitzPHP\Utilities\Iterable\Arr;
20
use InvalidArgumentException;
21
use Nette\Schema\Expect;
22
use Nette\Schema\Schema;
23
use ReflectionClass;
24
use ReflectionMethod;
25
26
class Config
27
{
28
    /**
29
     * Fichier de configuration déjà chargé
30
     */
31
    private static array $loaded = [];
32
33
	/**
34
	 * Different registrars decouverts.
35
	 * 
36
	 * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
37
	 */
38
	private static array $registrars = [];
39
40
    /**
41
     * Drapeau permettant de savoir si la config a deja ete initialiser
42
     */
43
    private static bool $initialized = false;
44
45
    private Configurator $configurator;
46
47
    public function __construct()
48
    {
49
        $this->configurator = new Configurator();
50
        $this->initialize();
51
    }
52
53
    /**
54
     * Détermine si une clé de configuration existe.
55
     */
56
    public function exists(string $key): bool
57
    {
58
        if (! $this->configurator->exists($key)) {
59
            $config = explode('.', $key);
60
            $this->load($config[0]);
61
62
            return $this->configurator->exists(implode('.', $config));
63
        }
64
65
        return true;
66
    }
67
68
    /**
69
     * Détermine s'il y'a une clé de configuration.
70
     */
71
    public function has(string $key): bool
72
    {
73
        return $this->exists($key);
74
    }
75
76
    /**
77
     * Détermine s'il manque une clé de configuration.
78
     */
79
    public function missing(string $key): bool
80
    {
81
        return ! $this->exists($key);
82
    }
83
84
    /**
85
     * Renvoyer une configuration de l'application
86
     *
87
     * @return mixed
88
     */
89
    public function get(string $key, mixed $default = null)
90
    {
91
        if ($this->exists($key)) {
92
            return $this->configurator->get($key);
93
        }
94
95
        if (func_num_args() > 1) {
96
            return $default;
97
        }
98
99
        $path = explode('.', $key);
100
101
        throw ConfigException::notFound(implode(' » ', $path));
102
    }
103
104
    /**
105
     * Définir une configuration de l'application
106
     *
107
     * @param mixed $value
108
     */
109
    public function set(string $key, $value)
110
    {
111
        $this->configurator->set($key, $value);
112
    }
113
114
    /**
115
     * Charger la configuration spécifique dans le scoope
116
     *
117
     * @param string|string[] $config
118
     */
119
    public function load($config, ?string $file = null, ?Schema $schema = null)
120
    {
121
        if (is_array($config)) {
122
            foreach ($config as $key => $value) {
123
                if (! is_string($value) || empty($value)) {
124
                    continue;
125
                }
126
                if (is_string($key)) {
127
                    $file = $value;
128
                    $conf = $key;
129
                } else {
130
                    $file = null;
131
                    $conf = $value;
132
                }
133
                self::load($conf, $file);
0 ignored issues
show
Bug Best Practice introduced by
The method BlitzPHP\Config\Config::load() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

133
                self::/** @scrutinizer ignore-call */ 
134
                      load($conf, $file);
Loading history...
134
            }
135
        } elseif (is_string($config) && ! isset(self::$loaded[$config])) {
136
            if (empty($file)) {
137
                $file = self::path($config);
138
            }
139
140
            $configurations = [];
141
            if (file_exists($file) && ! in_array($file, get_included_files(), true)) {
142
                $configurations = (array) require $file;
143
            }
144
145
			$configurations = Arr::merge(self::$registrars[$config] ?? [], $configurations);
146
147
            if (empty($schema)) {
148
                $schema = self::schema($config);
149
            }
150
151
            $this->configurator->addSchema($config, $schema, false);
152
            $this->configurator->merge([$config => $configurations]);
153
154
            self::$loaded[$config] = $file;
155
        }
156
    }
157
158
    /**
159
     * Affiche l'exception dû à la mauvaise definition d'une configuration
160
     *
161
     * @param array|string $accepts_values
162
     * @param string       $group          (app, data, database, etc.)
163
     */
164
    public static function exceptBadConfigValue(string $config_key, $accepts_values, string $group)
165
    {
166
        if (is_array($accepts_values)) {
167
            $accepts_values = '(Accept values: ' . implode('/', $accepts_values) . ')';
168
        } elseif (! is_string($accepts_values)) {
0 ignored issues
show
introduced by
The condition is_string($accepts_values) is always true.
Loading history...
169
            throw new InvalidArgumentException('Misuse of the method ' . __METHOD__);
170
        }
171
172
        throw new ConfigException("The '{$group}.{$config_key} configuration is not set correctly. {$accepts_values} \n Please edit '{" . self::path($group) . "}' file to correct it");
173
    }
174
175
    /**
176
     * Renvoie le chemin du fichier d'un groupe de configuration donné
177
     */
178
    public static function path(string $path): string
179
    {
180
        $path = preg_replace('#\.php$#', '', $path);
181
182
        if (file_exists($file = CONFIG_PATH . $path . '.php')) {
183
            return $file;
184
        }
185
186
        $paths = Services::locator()->search('Config/' . $path);
187
188
        if (isset($paths[0]) && file_exists($path[0])) {
189
            return $paths[0];
190
        }
191
192
        return '';
193
    }
194
195
    /**
196
     * Retrouve le schema de configuration d'un groupe
197
     */
198
    public static function schema(string $key): Schema
199
    {
200
        $file        = 'schemas' . DS . Helpers::ensureExt($key . '.config', 'php');
201
        $syst_schema = SYST_PATH . 'Constants' . DS . $file;
202
        $app_schema  = CONFIG_PATH . $file;
203
204
        if (file_exists($syst_schema)) {
205
            $schema = require $syst_schema;
206
        } elseif (file_exists($app_schema)) {
207
            $schema = require $app_schema;
208
        }
209
210
        if (empty($schema) || ! ($schema instanceof Schema)) {
211
            $schema = Expect::mixed();
212
        }
213
214
        return $schema;
215
    }
216
217
    /**
218
     * Initialiser la configuration du système avec les données des fichier de configuration
219
     */
220
    private function initialize()
221
    {
222
        if (self::$initialized) {
223
            return;
224
        }
225
226
		$this->loadRegistrar();
227
        $this->load(['app']);
228
229
        ini_set('log_errors', 1);
230
        ini_set('error_log', LOG_PATH . 'blitz-logs');
231
232
        $this->initializeURL();
233
        $this->initializeEnvironment();
234
        $this->initializeDebugbar();
235
236
        self::$initialized = true;
237
    }
238
239
	/**
240
	 * Charges les registrars disponible pour l'application.
241
	 * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
242
	 */
243
	private function loadRegistrar() 
244
	{
245
		$autoloader = new Autoloader(['psr4' => [APP_NAMESPACE => APP_PATH]]);
246
		$locator    = new Locator($autoloader->initialize());
247
248
		$registrarsFiles = $locator->search('Config/Registrar.php');
249
250
		foreach ($registrarsFiles as $file) {
251
			$class   = new ReflectionClass($locator->getClassname($file));
252
			$methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
253
254
			foreach ($methods as $method) {
255
				if (!($method->isPublic() && $method->isStatic())) {
256
					continue;
257
				}
258
259
				if (!is_array($result = $method->invoke(null))) {
260
					continue;
261
				}
262
263
				$name                    = $method->getName();
264
				self::$registrars[$name] = Arr::merge(self::$registrars[$name] ?? [], $result);
265
			}
266
		}
267
	}
268
269
    /**
270
     * Initialise l'URL
271
     */
272
    private function initializeURL()
273
    {
274
        $config = $this->get('app.base_url', 'auto');
275
276
        if ($config === 'auto' || empty($config)) {
277
            $config = rtrim(str_replace('\\', '/', Helpers::findBaseUrl()), '/');
278
        }
279
280
        $this->set('app.base_url', $config);
281
    }
282
283
    /**
284
     * Initialise l'environnement d'execution de l'application
285
     */
286
    private function initializeEnvironment()
287
    {
288
        $environment = $config = $this->get('app.environment');
289
290
        if ($config === 'auto') {
291
            $config = is_online() ? 'production' : 'development';
292
        } elseif ($config === 'dev') {
293
            $config = 'development';
294
        } elseif ($config === 'prod') {
295
            $config = 'production';
296
        }
297
298
        if ($config !== $environment) {
299
            $this->set('app.environment', $config);
300
        }
301
302
        switch ($config) {
303
            case 'development':
304
                error_reporting(-1);
305
                ini_set('display_errors', 1);
306
                break;
307
308
            case 'test':
309
            case 'production':
310
                ini_set('display_errors', 0);
311
                error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
312
                break;
313
314
            default:
315
                self::exceptBadConfigValue('environment', ['development', 'production', 'test', 'auto'], 'app');
316
        }
317
318
        defined('BLITZ_DEBUG') || define('BLITZ_DEBUG', $config !== 'production');
319
    }
320
321
    /**
322
     * Initialise les paramètres de la bar de debug
323
     */
324
    private function initializeDebugbar()
325
    {
326
        $config = $this->get('app.show_debugbar', 'auto');
327
328
        if (! in_array($config, ['auto', true, false], true)) {
329
            self::exceptBadConfigValue('show_debugbar', ['auto', true, false], 'app');
330
        }
331
332
        if ($config === 'auto') {
333
            $this->set('app.show_debugbar', ! is_online());
334
        }
335
    }
336
}
337