Test Failed
Push — main ( 57b19c...1fe2cd )
by Dimitri
15:57
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 8
            $config = explode('.', $key);
66 8
            $this->load($config[0]);
67
68 8
            return $this->configurator->exists(implode('.', $config));
69
        }
70
71 20
        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 4
        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 20
            return $this->configurator->get($key);
99
        }
100
101
        if (func_num_args() > 1) {
102 6
            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 6
        $path = explode('.', $key);
118 6
        $this->load($path[0]);
119
120 6
        $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 2
            $keys = array_keys(self::$originals);
132
        }
133
134
        foreach ($keys as $key) {
135 4
            $this->set($key, Arr::dataGet(self::$originals, $key));
136
137
            if (str_starts_with($key, 'app')) {
138 4
                $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): static
148
    {
149 2
        $this->load($key, null, $schema, true);
150
151 2
        return $this;
152
    }
153
154
    /**
155
     * Charger la configuration spécifique dans le scoope
156
     *
157
     * @param string|string[] $config
158
     */
159
    public function load($config, ?string $file = null, ?Schema $schema = null, bool $allow_empty = false)
160
    {
161
        if (is_array($config)) {
162
            foreach ($config as $key => $value) {
163
                if (is_string($key)) {
164
                    $file = $value;
165
                    $conf = $key;
166
                } else {
167 2
                    $file = null;
168 2
                    $conf = $value;
169
                }
170 2
                $this->load($conf, $file, null, $allow_empty);
171
            }
172
        } elseif (! isset(self::$loaded[$config])) {
173 6
            $file ??= self::path($config);
174 6
            $schema ??= self::schema($config);
175
176 6
            $configurations = [];
177
            if (file_exists($file) && ! in_array($file, get_included_files(), true)) {
178 4
                $configurations = (array) require $file;
179
            }
180
181 6
            $configurations = Arr::merge(self::$registrars[$config] ?? [], $configurations);
182
183
            if (empty($configurations) && ! $allow_empty && (empty($schema) || ! is_a($schema, Schema::class))) {
184 2
                return;
185
            }
186
187 6
            $this->configurator->addSchema($config, $schema ?: Expect::mixed(), false);
188 6
            $this->configurator->merge([$config => $configurations]);
189
190 6
            self::$loaded[$config]    = $file;
191 6
            self::$originals[$config] = $this->configurator->get($config);
192
        }
193
    }
194
195
    /**
196
     * Affiche l'exception dû à la mauvaise definition d'une configuration
197
     *
198
     * @param string $group (app, data, database, etc.)
199
     */
200
    public static function exceptBadConfigValue(string $config_key, array|string $accepts_values, string $group)
201
    {
202
        if (is_array($accepts_values)) {
0 ignored issues
show
introduced by
The condition is_array($accepts_values) is always true.
Loading history...
203
            $accepts_values = '(Accept values: ' . implode('/', $accepts_values) . ')';
204
        }
205
206
        throw new ConfigException("The '{$group}.{$config_key} configuration is not set correctly. {$accepts_values} \n Please edit '{" . self::path($group) . "}' file to correct it");
207
    }
208
209
    /**
210
     * Renvoie le chemin du fichier d'un groupe de configuration donné
211
     */
212
    public static function path(string $path): string
213
    {
214 8
        $path = preg_replace('#\.php$#', '', $path);
215
216
        if (file_exists($file = CONFIG_PATH . $path . '.php')) {
217 6
            return $file;
218
        }
219
220 4
        $paths = Services::locator()->search('Config/' . $path);
221
222
        if (isset($paths[0]) && file_exists($path[0])) {
223 4
            return $paths[0];
224
        }
225
226 4
        return '';
227
    }
228
229
    /**
230
     * Retrouve le schema de configuration d'un groupe
231
     */
232
    public static function schema(string $key): ?Schema
233
    {
234 8
        $file        = 'schemas' . DS . Helpers::ensureExt($key . '.config', 'php');
235 8
        $syst_schema = SYST_PATH . 'Constants' . DS . $file;
236 8
        $app_schema  = CONFIG_PATH . $file;
237
238
        if (file_exists($syst_schema)) {
239 2
            $schema = require $syst_schema;
240
        } elseif (file_exists($app_schema)) {
241
            $schema = require $app_schema;
242
        }
243
244 8
        return $schema ?? null;
245
    }
246
247
    /**
248
     * Initialiser la configuration du système avec les données des fichier de configuration
249
     */
250
    private function initialize()
251
    {
252
        if (self::$initialized) {
253
            return;
254
        }
255
256
        $this->loadRegistrar();
257
        $this->load(['app']);
258
259
        ini_set('log_errors', 1);
260
        ini_set('error_log', LOG_PATH . 'blitz-logs');
261
262
        $this->initializeAutoDetect();
263
264
        self::$initialized = true;
265
    }
266
267
    /**
268
     * Charges les registrars disponible pour l'application.
269
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
270
     */
271
    private function loadRegistrar()
272
    {
273
        $autoloader = new Autoloader(['psr4' => [APP_NAMESPACE => APP_PATH]]);
274
        $locator    = new Locator($autoloader->initialize());
275
276
        $registrarsFiles = $locator->search('Config/Registrar.php');
277
278
        foreach ($registrarsFiles as $file) {
279
            if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
280
                continue;
281
            }
282
283
            $class   = new ReflectionClass($classname);
284
            $methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
285
286
            foreach ($methods as $method) {
287
                if (! ($method->isPublic() && $method->isStatic())) {
288
                    continue;
289
                }
290
291
                if (! is_array($result = $method->invoke(null))) {
292
                    continue;
293
                }
294
295
                $name                    = $method->getName();
296
                self::$registrars[$name] = Arr::merge(self::$registrars[$name] ?? [], $result);
297
            }
298
        }
299
    }
300
301
    /**
302
     * Initialise l'URL
303
     */
304
    private function initializeURL()
305
    {
306 4
        $config = $this->get('app.base_url', 'auto');
307
308
        if ($config === 'auto' || empty($config)) {
309 4
            $config = rtrim(str_replace('\\', '/', Helpers::findBaseUrl()), '/');
310
        }
311
312 4
        $this->set('app.base_url', $config);
313
    }
314
315
    /**
316
     * Initialise l'environnement d'execution de l'application
317
     */
318
    private function initializeEnvironment()
319
    {
320 4
        $environment = $config = $this->get('app.environment');
321
322
        $config = match ($config) {
323
            'auto'  => is_online() ? 'production' : 'development',
324
            'dev'   => 'development',
325
            'prod'  => 'production',
326
            'test'  => 'testing',
327
            default => $config,
328
        };
329
330
        if ($config !== $environment) {
331 4
            $this->set('app.environment', $config);
332
        }
333
334
        switch ($config) {
335
            case 'development':
336 4
                error_reporting(-1);
337
                ini_set('display_errors', 1);
338
                break;
339
340
            case 'testing':
341
            case 'production':
342 4
                ini_set('display_errors', 0);
343 4
                error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
344 4
                break;
345
346
            default:
347
                self::exceptBadConfigValue('environment', ['development', 'production', 'testing', 'auto'], 'app');
348
        }
349
350 4
        defined('BLITZ_DEBUG') || define('BLITZ_DEBUG', $config !== 'production');
351
    }
352
353
    /**
354
     * Initialise les paramètres de la bar de debug
355
     */
356
    private function initializeDebugbar()
357
    {
358 4
        $config = $this->get('app.show_debugbar', 'auto');
359
360
        if (! in_array($config, ['auto', true, false], true)) {
361 4
            self::exceptBadConfigValue('show_debugbar', ['auto', true, false], 'app');
362
        }
363
364
        if ($config === 'auto') {
365 4
            $this->set('app.show_debugbar', ! is_online());
366
        }
367
    }
368
369
    /**
370
     * Initialise les donnees qui ont une valeur definie a "auto" et donc dependent de certains facteurs
371
     */
372
    private function initializeAutoDetect(): void
373
    {
374 4
        $this->initializeURL();
375 4
        $this->initializeEnvironment();
376 4
        $this->initializeDebugbar();
377
    }
378
}
379