Passed
Push — main ( 6ad81f...1b5a5a )
by Dimitri
12:10 queued 08:06
created

Config::load()   B

Complexity

Conditions 11
Paths 8

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 11.2865

Importance

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

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\Exceptions\ConfigException;
17
use BlitzPHP\Utilities\Helpers;
18
use BlitzPHP\Utilities\Iterable\Arr;
19
use Nette\Schema\Expect;
20
use Nette\Schema\Schema;
21
use ReflectionClass;
22
use ReflectionMethod;
23
24
class Config
25
{
26
    /**
27
     * Fichier de configuration déjà chargé
28
     */
29
    private static array $loaded = [];
30
31
    /**
32
     * Configurations originales issues des fichiers de configuration
33
     *
34
     * Permet de réinitialiser les configuration par défaut au cas où on aurrait fait des modifications à la volée
35
     */
36
    private static array $originals = [];
37
38
    /**
39
     * Different registrars decouverts.
40
     *
41
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
42
     */
43
    private static array $registrars = [];
44
45
    /**
46
     *  La découverte des modules est-elle terminée ?
47
     */
48
    protected static bool $didDiscovery = false;
49
50
    /**
51
     *  Le module discovery fonctionne-t-il ou non ?
52
     */
53
    protected static bool $discovering = false;
54
55
    /**
56
     * Le traitement du fichier Registrar pour le message d'erreur.
57
     */
58
    protected static string $registrarFile = '';
59
60
    /**
61
     * Drapeau permettant de savoir si la config a deja ete initialiser
62
     */
63
    private static bool $initialized = false;
64
65
    private readonly Configurator $configurator;
66
67
    public function __construct()
68
    {
69
        $this->configurator = new Configurator();
0 ignored issues
show
Bug introduced by
The property configurator is declared read-only in BlitzPHP\Config\Config.
Loading history...
70
        $this->initialize();
71
    }
72
73
    /**
74
     * Détermine si une clé de configuration existe.
75
     */
76
    public function exists(string $key): bool
77
    {
78
        if (! $this->configurator->exists($key)) {
79 20
            $config = explode('.', $key);
80 20
            $this->load($config[0]);
0 ignored issues
show
Bug introduced by
$config[0] of type string is incompatible with the type BlitzPHP\Config\list expected by parameter $config of BlitzPHP\Config\Config::load(). ( Ignorable by Annotation )

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

80
            $this->load(/** @scrutinizer ignore-type */ $config[0]);
Loading history...
81
82 20
            return $this->configurator->exists(implode('.', $config));
83
        }
84
85 219
        return true;
86
    }
87
88
    /**
89
     * Détermine s'il y'a une clé de configuration.
90
     */
91
    public function has(string $key): bool
92
    {
93 4
        return $this->exists($key);
94
    }
95
96
    /**
97
     * Détermine s'il manque une clé de configuration.
98
     */
99
    public function missing(string $key): bool
100
    {
101 2
        return ! $this->exists($key);
102
    }
103
104
    /**
105
     * Renvoyer une configuration de l'application
106
     *
107
     * @return mixed
108
     */
109
    public function get(string $key, mixed $default = null)
110
    {
111
        if ($this->exists($key)) {
112 223
            return $this->configurator->get($key);
113
        }
114
115
        if (func_num_args() > 1) {
116 10
            return $default;
117
        }
118
119 2
        $path = explode('.', $key);
120
121 2
        throw ConfigException::notFound(implode(' » ', $path));
122
    }
123
124
    /**
125
     * Définir une configuration de l'application
126
     *
127
     * @param mixed $value
128
     */
129
    public function set(string $key, $value)
130
    {
131 36
        $path = explode('.', $key);
132 36
        $this->load($path[0]);
0 ignored issues
show
Bug introduced by
$path[0] of type string is incompatible with the type BlitzPHP\Config\list expected by parameter $config of BlitzPHP\Config\Config::load(). ( Ignorable by Annotation )

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

132
        $this->load(/** @scrutinizer ignore-type */ $path[0]);
Loading history...
133
134 36
        $this->configurator->set($key, $value);
135
    }
136
137
    /**
138
     * Reinitialise une configuration en fonction des donnees initiales issues des fichiers de configurations
139
     */
140
    public function reset(array|string|null $keys = null): void
141
    {
142 18
        $keys = null !== $keys ? (array) $keys : array_keys(self::$originals);
143
144
        foreach ($keys as $key) {
145 18
            $this->set($key, Arr::dataGet(self::$originals, $key));
146
147
            if (str_starts_with($key, 'app')) {
148 6
                $this->initializeAutoDetect();
149
            }
150
        }
151
    }
152
153
    /**
154
     * Rend disponible un groupe de configuration qui n'existe pas (pas de fichier de configuration)
155
     * Ceci est notament utilse pour definir des configurations à la volée
156
     */
157
    public function ghost(array|string $key, array|Schema|null $structure = null): static
158
    {
159 4
        $schema = is_array($structure) ? Expect::mixed($structure) : $structure;
160
161 4
        $this->load($key, null, $schema, true);
0 ignored issues
show
Bug introduced by
$key of type array is incompatible with the type BlitzPHP\Config\list expected by parameter $config of BlitzPHP\Config\Config::load(). ( Ignorable by Annotation )

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

161
        $this->load(/** @scrutinizer ignore-type */ $key, null, $schema, true);
Loading history...
162
163 4
        return $this;
164
    }
165
166
    /**
167
     * Charger la configuration spécifique dans le scoope
168
     *
169
     * @param list<string>|string $config
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Config\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
170
     */
171
    public function load($config, ?string $file = null, ?Schema $schema = null, bool $allow_empty = false)
172
    {
173
        if (is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always false.
Loading history...
174
            foreach ($config as $key => $value) {
175
                if (is_string($key)) {
176
                    $file = $value;
177
                    $conf = $key;
178
                } else {
179 2
                    $file = null;
180 2
                    $conf = $value;
181
                }
182 2
                $this->load($conf, $file, null, $allow_empty);
183
            }
184
        } elseif (! isset(self::$loaded[$config])) {
185 16
            $file ??= self::path($config);
186 16
            $schema ??= self::schema($config);
187
188 16
            $configurations = [];
189
            if (file_exists($file) && ! in_array($file, get_included_files(), true)) {
190 10
                $configurations = (array) require $file;
191
            }
192
193 16
            $configurations = Arr::merge(self::$registrars[$config] ?? [], $configurations);
194
195
            if (empty($configurations) && ! $allow_empty && ! is_a($schema, Schema::class, true)) {
196 2
                return;
197
            }
198
199 16
            $this->configurator->addSchema($config, $schema ?: Expect::mixed(), false);
200 16
            $this->configurator->merge([$config => $configurations]);
201
202 16
            self::$loaded[$config]    = $file;
203 16
            self::$originals[$config] = $this->configurator->get($config);
204
        }
205
    }
206
207
    /**
208
     * Affiche l'exception dû à la mauvaise definition d'une configuration
209
     *
210
     * @param string $group (app, data, database, etc.)
211
     */
212
    public static function exceptBadConfigValue(string $config_key, array|string $accepts_values, string $group)
213
    {
214
        if (is_array($accepts_values)) {
0 ignored issues
show
introduced by
The condition is_array($accepts_values) is always true.
Loading history...
215
            $accepts_values = '(Accept values: ' . implode('/', $accepts_values) . ')';
216
        }
217
218
        throw new ConfigException("The '{$group}.{$config_key} configuration is not set correctly. {$accepts_values} \n Please edit '{" . self::path($group) . "}' file to correct it");
219
    }
220
221
    /**
222
     * Renvoie le chemin du fichier d'un groupe de configuration donné
223
     */
224
    public static function path(string $path): string
225
    {
226 18
        $path = preg_replace('#\.php$#', '', $path);
227
228
        if (file_exists($file = CONFIG_PATH . $path . '.php')) {
229 12
            return $file;
230
        }
231
232 8
        $paths = service('locator')->search('Config/' . $path);
233
234
        if (isset($paths[0]) && file_exists($path[0])) {
235 8
            return $paths[0];
236
        }
237
238 8
        return '';
239
    }
240
241
    /**
242
     * Retrouve le schema de configuration d'un groupe
243
     */
244
    public static function schema(string $key): ?Schema
245
    {
246 18
        $file        = 'schemas' . DS . Helpers::ensureExt($key . '.config', 'php');
247 18
        $syst_schema = SYST_PATH . 'Constants' . DS . $file;
248 18
        $app_schema  = CONFIG_PATH . $file;
249
250
        if (file_exists($syst_schema)) {
251 8
            $schema = require $syst_schema;
252
        } elseif (file_exists($app_schema)) {
253
            $schema = require $app_schema;
254
        } else {
255 12
            $paths = service('locator')->search('Config/schemas/' . $key);
256
257
            if (isset($paths[0]) && file_exists($paths[0])) {
258 12
                $schema = require $paths[0];
259
            }
260
        }
261
262 18
        return $schema ?? null;
263
    }
264
265
    /**
266
     * Initialiser la configuration du système avec les données des fichier de configuration
267
     */
268
    private function initialize()
269
    {
270
        if (self::$initialized) {
271
            return;
272
        }
273
274
        $this->loadRegistrar();
275
        $this->load(['app']);
0 ignored issues
show
Bug introduced by
array('app') of type array<integer,string> is incompatible with the type BlitzPHP\Config\list expected by parameter $config of BlitzPHP\Config\Config::load(). ( Ignorable by Annotation )

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

275
        $this->load(/** @scrutinizer ignore-type */ ['app']);
Loading history...
276
277
        ini_set('log_errors', 1);
278
        ini_set('error_log', LOG_PATH . 'blitz-logs');
279
280
        $this->initializeAutoDetect();
281
282
        self::$initialized = true;
283
    }
284
285
    /**
286
     * Charges les registrars disponible pour l'application.
287
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
288
     */
289
    private function loadRegistrar()
290
    {
291
        if (static::$didDiscovery) {
292
            return;
293
        }
294
295
        // La decouverte doit etre complete pres la premiere initalisation de la classe.
296
        if (static::$discovering) {
297
            throw new ConfigException(
298
                'Pendant la découverte automatique des Registrars,'
299
                . ' "' . static::class . '" a été re-éxecuté.'
300
                . ' "' . clean_path(static::$registrarFile) . '" doit avoir un mauvais code.'
301
            );
302
        }
303
304
        static::$discovering = true;
305
306
        $autoloader = new Autoloader(['psr4' => [APP_NAMESPACE => APP_PATH]]);
307
        $locator    = new Locator($autoloader->initialize());
308
309
        $registrarsFiles = $locator->search('Config/Registrar.php');
310
311
        foreach ($registrarsFiles as $file) {
312
            // Enregistre le fichier pour le message d'erreur.
313
            static::$registrarFile = $file;
314
315
            if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
316
                continue;
317
            }
318
319
            $class   = new ReflectionClass($classname);
320
            $methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
321
322
            foreach ($methods as $method) {
323
                if (! ($method->isPublic() && $method->isStatic())) {
324
                    continue;
325
                }
326
327
                if (! is_array($result = $method->invoke(null))) {
328
                    continue;
329
                }
330
331
                $name                    = $method->getName();
332
                self::$registrars[$name] = Arr::merge(self::$registrars[$name] ?? [], $result);
333
            }
334
        }
335
336
        static::$didDiscovery = true;
337
        static::$discovering  = false;
338
    }
339
340
    /**
341
     * Initialise l'URL
342
     */
343
    private function initializeURL()
344
    {
345 6
        $config = $this->get('app.base_url', 'auto');
346
347
        if ($config === 'auto' || empty($config)) {
348 6
            $config = rtrim(str_replace('\\', '/', Helpers::findBaseUrl()), '/');
349
        }
350
351 6
        $this->set('app.base_url', $config);
352
    }
353
354
    /**
355
     * Initialise l'environnement d'execution de l'application
356
     */
357
    private function initializeEnvironment()
358
    {
359 6
        $environment = $config = $this->get('app.environment');
360
361
        $config = match ($config) {
362
            'auto'  => is_online() ? 'production' : 'development',
363
            'dev'   => 'development',
364
            'prod'  => 'production',
365
            'test'  => 'testing',
366
            default => $config,
367
        };
368
369
        if ($config !== $environment) {
370 6
            $this->set('app.environment', $config);
371
        }
372
373
        switch ($config) {
374
            case 'development':
375 6
                error_reporting(-1);
376
                ini_set('display_errors', 1);
377
                break;
378
379
            case 'testing':
380
            case 'production':
381 6
                ini_set('display_errors', 0);
382 6
                error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
383 6
                break;
384
385
            default:
386
                self::exceptBadConfigValue('environment', ['development', 'production', 'testing', 'auto'], 'app');
387
        }
388
389 6
        defined('BLITZ_DEBUG') || define('BLITZ_DEBUG', $config !== 'production');
390
    }
391
392
    /**
393
     * Initialise les paramètres de la bar de debug
394
     */
395
    private function initializeDebugbar()
396
    {
397 6
        $config = $this->get('app.show_debugbar', 'auto');
398
399
        if (! in_array($config, ['auto', true, false], true)) {
400 6
            self::exceptBadConfigValue('show_debugbar', ['auto', true, false], 'app');
401
        }
402
403
        if ($config === 'auto') {
404 4
            $this->set('app.show_debugbar', ! is_online());
405
        }
406
    }
407
408
    /**
409
     * Initialise les donnees qui ont une valeur definie a "auto" et donc dependent de certains facteurs
410
     */
411
    private function initializeAutoDetect(): void
412
    {
413 6
        $this->initializeURL();
414 6
        $this->initializeEnvironment();
415 6
        $this->initializeDebugbar();
416
    }
417
}
418