Passed
Push — main ( 1fe2cd...c1deb1 )
by Dimitri
04:10
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 16
            $config = explode('.', $key);
66 16
            $this->load($config[0]);
67
68 16
            return $this->configurator->exists(implode('.', $config));
69
        }
70
71 171
        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 175
            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 22
        $path = explode('.', $key);
118 22
        $this->load($path[0]);
119
120 22
        $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 4
        $this->load($key, null, $schema, true);
150
151 4
        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 16
            $file ??= self::path($config);
174 16
            $schema ??= self::schema($config);
175
176 16
            $configurations = [];
177
            if (file_exists($file) && ! in_array($file, get_included_files(), true)) {
178 10
                $configurations = (array) require $file;
179
            }
180
181 16
            $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 16
            $this->configurator->addSchema($config, $schema ?: Expect::mixed(), false);
188 16
            $this->configurator->merge([$config => $configurations]);
189
190 16
            self::$loaded[$config]    = $file;
191 16
            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 18
        $path = preg_replace('#\.php$#', '', $path);
215
216
        if (file_exists($file = CONFIG_PATH . $path . '.php')) {
217 12
            return $file;
218
        }
219
220 8
        $paths = Services::locator()->search('Config/' . $path);
221
222
        if (isset($paths[0]) && file_exists($path[0])) {
223 8
            return $paths[0];
224
        }
225
226 8
        return '';
227
    }
228
229
    /**
230
     * Retrouve le schema de configuration d'un groupe
231
     */
232
    public static function schema(string $key): ?Schema
233
    {
234 18
        $file        = 'schemas' . DS . Helpers::ensureExt($key . '.config', 'php');
235 18
        $syst_schema = SYST_PATH . 'Constants' . DS . $file;
236 18
        $app_schema  = CONFIG_PATH . $file;
237
238
        if (file_exists($syst_schema)) {
239 8
            $schema = require $syst_schema;
240
        } elseif (file_exists($app_schema)) {
241
            $schema = require $app_schema;
242
        } else {
243 12
            $paths = Services::locator()->search('Config/schemas/' . $key);
244
245
            if (isset($paths[0]) && file_exists($paths[0])) {
246 12
                $schema = require $paths[0];
247
            }
248
        }
249
250 18
        return $schema ?? null;
251
    }
252
253
    /**
254
     * Initialiser la configuration du système avec les données des fichier de configuration
255
     */
256
    private function initialize()
257
    {
258
        if (self::$initialized) {
259
            return;
260
        }
261
262
        $this->loadRegistrar();
263
        $this->load(['app']);
264
265
        ini_set('log_errors', 1);
266
        ini_set('error_log', LOG_PATH . 'blitz-logs');
267
268
        $this->initializeAutoDetect();
269
270
        self::$initialized = true;
271
    }
272
273
    /**
274
     * Charges les registrars disponible pour l'application.
275
     * Les registrars sont des mecanismes permettant aux packages externe de definir un elements de configuration
276
     */
277
    private function loadRegistrar()
278
    {
279
        $autoloader = new Autoloader(['psr4' => [APP_NAMESPACE => APP_PATH]]);
280
        $locator    = new Locator($autoloader->initialize());
281
282
        $registrarsFiles = $locator->search('Config/Registrar.php');
283
284
        foreach ($registrarsFiles as $file) {
285
            if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
286
                continue;
287
            }
288
289
            $class   = new ReflectionClass($classname);
290
            $methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
291
292
            foreach ($methods as $method) {
293
                if (! ($method->isPublic() && $method->isStatic())) {
294
                    continue;
295
                }
296
297
                if (! is_array($result = $method->invoke(null))) {
298
                    continue;
299
                }
300
301
                $name                    = $method->getName();
302
                self::$registrars[$name] = Arr::merge(self::$registrars[$name] ?? [], $result);
303
            }
304
        }
305
    }
306
307
    /**
308
     * Initialise l'URL
309
     */
310
    private function initializeURL()
311
    {
312 4
        $config = $this->get('app.base_url', 'auto');
313
314
        if ($config === 'auto' || empty($config)) {
315 4
            $config = rtrim(str_replace('\\', '/', Helpers::findBaseUrl()), '/');
316
        }
317
318 4
        $this->set('app.base_url', $config);
319
    }
320
321
    /**
322
     * Initialise l'environnement d'execution de l'application
323
     */
324
    private function initializeEnvironment()
325
    {
326 4
        $environment = $config = $this->get('app.environment');
327
328
        $config = match ($config) {
329
            'auto'  => is_online() ? 'production' : 'development',
330
            'dev'   => 'development',
331
            'prod'  => 'production',
332
            'test'  => 'testing',
333
            default => $config,
334
        };
335
336
        if ($config !== $environment) {
337 4
            $this->set('app.environment', $config);
338
        }
339
340
        switch ($config) {
341
            case 'development':
342 4
                error_reporting(-1);
343
                ini_set('display_errors', 1);
344
                break;
345
346
            case 'testing':
347
            case 'production':
348 4
                ini_set('display_errors', 0);
349 4
                error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
350 4
                break;
351
352
            default:
353
                self::exceptBadConfigValue('environment', ['development', 'production', 'testing', 'auto'], 'app');
354
        }
355
356 4
        defined('BLITZ_DEBUG') || define('BLITZ_DEBUG', $config !== 'production');
357
    }
358
359
    /**
360
     * Initialise les paramètres de la bar de debug
361
     */
362
    private function initializeDebugbar()
363
    {
364 4
        $config = $this->get('app.show_debugbar', 'auto');
365
366
        if (! in_array($config, ['auto', true, false], true)) {
367 4
            self::exceptBadConfigValue('show_debugbar', ['auto', true, false], 'app');
368
        }
369
370
        if ($config === 'auto') {
371 4
            $this->set('app.show_debugbar', ! is_online());
372
        }
373
    }
374
375
    /**
376
     * Initialise les donnees qui ont une valeur definie a "auto" et donc dependent de certains facteurs
377
     */
378
    private function initializeAutoDetect(): void
379
    {
380 4
        $this->initializeURL();
381 4
        $this->initializeEnvironment();
382 4
        $this->initializeDebugbar();
383
    }
384
}
385