Passed
Push — main ( f9905b...3d6ec5 )
by Dimitri
04:38
created

Config::load()   C

Complexity

Conditions 12
Paths 8

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 12.341

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 12
eloc 22
c 3
b 0
f 0
nc 8
nop 4
dl 0
loc 33
ccs 13
cts 15
cp 0.8667
crap 12.341
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Nette\Schema\Expect;
21
use Nette\Schema\Schema;
22
use ReflectionClass;
23
use ReflectionMethod;
24
25
class Config
26
{
27
    /**
28
     * Fichier de configuration déjà chargé
29
     */
30
    private static array $loaded = [];
31
32
    /**
33
     * Configurations originales issues des fichiers de configuration
34
     *
35
     * Permet de réinitialiser les configuration par défaut au cas où on aurrait fait des modifications à la volée
36
     */
37
    private static array $originals = [];
38
39
    /**
40
     * Different registrars decouverts.
41
     *
42
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
43
     */
44
    private static array $registrars = [];
45
46
    /**
47
     * Drapeau permettant de savoir si la config a deja ete initialiser
48
     */
49
    private static bool $initialized = false;
50
51
    private Configurator $configurator;
52
53
    public function __construct()
54
    {
55
        $this->configurator = new Configurator();
56
        $this->initialize();
57
    }
58
59
    /**
60
     * Détermine si une clé de configuration existe.
61
     */
62
    public function exists(string $key): bool
63
    {
64
        if (! $this->configurator->exists($key)) {
65 14
            $config = explode('.', $key);
66 14
            $this->load($config[0]);
67
68 14
            return $this->configurator->exists(implode('.', $config));
69
        }
70
71 124
        return true;
72
    }
73
74
    /**
75
     * Détermine s'il y'a une clé de configuration.
76
     */
77
    public function has(string $key): bool
78
    {
79 2
        return $this->exists($key);
80
    }
81
82
    /**
83
     * Détermine s'il manque une clé de configuration.
84
     */
85
    public function missing(string $key): bool
86
    {
87 2
        return ! $this->exists($key);
88
    }
89
90
    /**
91
     * Renvoyer une configuration de l'application
92
     *
93
     * @return mixed
94
     */
95
    public function get(string $key, mixed $default = null)
96
    {
97
        if ($this->exists($key)) {
98 128
            return $this->configurator->get($key);
99
        }
100
101
        if (func_num_args() > 1) {
102 4
            return $default;
103
        }
104
105 2
        $path = explode('.', $key);
106
107 2
        throw ConfigException::notFound(implode(' » ', $path));
108
    }
109
110
    /**
111
     * Définir une configuration de l'application
112
     *
113
     * @param mixed $value
114
     */
115
    public function set(string $key, $value)
116
    {
117 16
        $path = explode('.', $key);
118 16
        $this->load($path[0]);
119
120 16
        $this->configurator->set($key, $value);
121
    }
122
123
    /**
124
     * Reinitialise une configuration en fonction des donnees initiales issues des fichiers de configurations
125
     */
126
    public function reset(null|array|string $keys = null): void
127
    {
128
        if (null !== $keys) {
129 2
            $keys = (array) $keys;
130
        } else {
131
            $keys = array_keys(self::$originals);
132
        }
133
134
        foreach ($keys as $key) {
135 2
            $this->set($key, Arr::dataGet(self::$originals, $key));
136
137
            if (str_starts_with($key, 'app')) {
138 2
                $this->initializeAutoDetect();
139
            }
140
        }
141
    }
142
143
    /**
144
     * Rend disponible un groupe de configuration qui n'existe pas (pas de fichier de configuration)
145
     * Ceci est notament utilse pour definir des configurations à la volée
146
     */
147
    public function ghost(array|string $key, ?Schema $schema = null): void
148
    {
149 2
        $this->load($key, null, $schema, true);
150
    }
151
152
    /**
153
     * Charger la configuration spécifique dans le scoope
154
     *
155
     * @param string|string[] $config
156
     */
157
    public function load($config, ?string $file = null, ?Schema $schema = null, bool $allow_empty = false)
158
    {
159
        if (is_array($config)) {
160
            foreach ($config as $key => $value) {
161
                if (is_string($key)) {
162
                    $file = $value;
163
                    $conf = $key;
164
                } else {
165 2
                    $file = null;
166 2
                    $conf = $value;
167
                }
168 2
                $this->load($conf, $file, null, $allow_empty);
169
            }
170
        } elseif (! isset(self::$loaded[$config])) {
171 14
            $file ??= self::path($config);
172 14
            $schema ??= self::schema($config);
173
174 14
            $configurations = [];
175
            if (file_exists($file) && ! in_array($file, get_included_files(), true)) {
176 10
                $configurations = (array) require $file;
177
            }
178
179 14
            $configurations = Arr::merge(self::$registrars[$config] ?? [], $configurations);
180
181
            if (empty($configurations) && ! $allow_empty && (empty($schema) || ! is_a($schema, Schema::class))) {
182 2
                return;
183
            }
184
185 14
            $this->configurator->addSchema($config, $schema ?: Expect::mixed(), false);
186 14
            $this->configurator->merge([$config => $configurations]);
187
188 14
            self::$loaded[$config]    = $file;
189 14
            self::$originals[$config] = $this->configurator->get($config);
190
        }
191
    }
192
193
    /**
194
     * Affiche l'exception dû à la mauvaise definition d'une configuration
195
     *
196
     * @param string $group (app, data, database, etc.)
197
     */
198
    public static function exceptBadConfigValue(string $config_key, array|string $accepts_values, string $group)
199
    {
200
        if (is_array($accepts_values)) {
0 ignored issues
show
introduced by
The condition is_array($accepts_values) is always true.
Loading history...
201
            $accepts_values = '(Accept values: ' . implode('/', $accepts_values) . ')';
202
        }
203
204
        throw new ConfigException("The '{$group}.{$config_key} configuration is not set correctly. {$accepts_values} \n Please edit '{" . self::path($group) . "}' file to correct it");
205
    }
206
207
    /**
208
     * Renvoie le chemin du fichier d'un groupe de configuration donné
209
     */
210
    public static function path(string $path): string
211
    {
212 16
        $path = preg_replace('#\.php$#', '', $path);
213
214
        if (file_exists($file = CONFIG_PATH . $path . '.php')) {
215 12
            return $file;
216
        }
217
218 6
        $paths = Services::locator()->search('Config/' . $path);
219
220
        if (isset($paths[0]) && file_exists($path[0])) {
221 6
            return $paths[0];
222
        }
223
224 6
        return '';
225
    }
226
227
    /**
228
     * Retrouve le schema de configuration d'un groupe
229
     */
230
    public static function schema(string $key): ?Schema
231
    {
232 16
        $file        = 'schemas' . DS . Helpers::ensureExt($key . '.config', 'php');
233 16
        $syst_schema = SYST_PATH . 'Constants' . DS . $file;
234 16
        $app_schema  = CONFIG_PATH . $file;
235
236
        if (file_exists($syst_schema)) {
237 8
            $schema = require $syst_schema;
238
        } elseif (file_exists($app_schema)) {
239
            $schema = require $app_schema;
240
        }
241
242 16
        return $schema ?? null;
243
    }
244
245
    /**
246
     * Initialiser la configuration du système avec les données des fichier de configuration
247
     */
248
    private function initialize()
249
    {
250
        if (self::$initialized) {
251
            return;
252
        }
253
254
        $this->loadRegistrar();
255
        $this->load(['app']);
256
257
        ini_set('log_errors', 1);
258
        ini_set('error_log', LOG_PATH . 'blitz-logs');
259
260
        $this->initializeAutoDetect();
261
262
        self::$initialized = true;
263
    }
264
265
    /**
266
     * Charges les registrars disponible pour l'application.
267
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
268
     */
269
    private function loadRegistrar()
270
    {
271
        $autoloader = new Autoloader(['psr4' => [APP_NAMESPACE => APP_PATH]]);
272
        $locator    = new Locator($autoloader->initialize());
273
274
        $registrarsFiles = $locator->search('Config/Registrar.php');
275
276
        foreach ($registrarsFiles as $file) {
277
            if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
278
                continue;
279
            }
280
281
            $class   = new ReflectionClass($classname);
282
            $methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
283
284
            foreach ($methods as $method) {
285
                if (! ($method->isPublic() && $method->isStatic())) {
286
                    continue;
287
                }
288
289
                if (! is_array($result = $method->invoke(null))) {
290
                    continue;
291
                }
292
293
                $name                    = $method->getName();
294
                self::$registrars[$name] = Arr::merge(self::$registrars[$name] ?? [], $result);
295
            }
296
        }
297
    }
298
299
    /**
300
     * Initialise l'URL
301
     */
302
    private function initializeURL()
303
    {
304 2
        $config = $this->get('app.base_url', 'auto');
305
306
        if ($config === 'auto' || empty($config)) {
307 2
            $config = rtrim(str_replace('\\', '/', Helpers::findBaseUrl()), '/');
308
        }
309
310 2
        $this->set('app.base_url', $config);
311
    }
312
313
    /**
314
     * Initialise l'environnement d'execution de l'application
315
     */
316
    private function initializeEnvironment()
317
    {
318 2
        $environment = $config = $this->get('app.environment');
319
320
        $config = match ($config) {
321
            'auto'  => is_online() ? 'production' : 'development',
322
            'dev'   => 'development',
323
            'prod'  => 'production',
324
            'test'  => 'testing',
325
            default => $config,
326
        };
327
328
        if ($config !== $environment) {
329 2
            $this->set('app.environment', $config);
330
        }
331
332
        switch ($config) {
333
            case 'development':
334 2
                error_reporting(-1);
335
                ini_set('display_errors', 1);
336
                break;
337
338
            case 'testing':
339
            case 'production':
340 2
                ini_set('display_errors', 0);
341 2
                error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
342 2
                break;
343
344
            default:
345
                self::exceptBadConfigValue('environment', ['development', 'production', 'testing', 'auto'], 'app');
346
        }
347
348 2
        defined('BLITZ_DEBUG') || define('BLITZ_DEBUG', $config !== 'production');
349
    }
350
351
    /**
352
     * Initialise les paramètres de la bar de debug
353
     */
354
    private function initializeDebugbar()
355
    {
356 2
        $config = $this->get('app.show_debugbar', 'auto');
357
358
        if (! in_array($config, ['auto', true, false], true)) {
359 2
            self::exceptBadConfigValue('show_debugbar', ['auto', true, false], 'app');
360
        }
361
362
        if ($config === 'auto') {
363 2
            $this->set('app.show_debugbar', ! is_online());
364
        }
365
    }
366
367
    /**
368
     * Initialise les donnees qui ont une valeur definie a "auto" et donc dependent de certains facteurs
369
     */
370
    private function initializeAutoDetect(): void
371
    {
372 2
        $this->initializeURL();
373 2
        $this->initializeEnvironment();
374 2
        $this->initializeDebugbar();
375
    }
376
}
377