Passed
Push — main ( 537ce5...d0f73f )
by Dimitri
04:36
created

Config::load()   C

Complexity

Conditions 15
Paths 9

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 16.7999

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 23
c 2
b 0
f 0
nc 9
nop 4
dl 0
loc 35
ccs 12
cts 15
cp 0.8
crap 16.7999
rs 5.9166

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 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 10
            $config = explode('.', $key);
60 10
            $this->load($config[0]);
61
62 10
            return $this->configurator->exists(implode('.', $config));
63
        }
64
65 113
        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 2
        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 2
        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 117
            return $this->configurator->get($key);
93
        }
94
95
        if (func_num_args() > 1) {
96 2
            return $default;
97
        }
98
99 2
        $path = explode('.', $key);
100
101 2
        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 13
        $path = explode('.', $key);
112 13
        $this->load($path[0]);
113
114 13
        $this->configurator->set($key, $value);
115
    }
116
117
    /**
118
     * Rend disponible un groupe de configuration qui n'existe pas (pas de fichier de configuration)
119
     * Ceci est notament utilse pour definir des configurations à la volée
120
     */
121
    public function ghost(array|string $key, ?Schema $schema = null): void
122
    {
123 2
        $this->load($key, null, $schema, true);
124
    }
125
126
    /**
127
     * Charger la configuration spécifique dans le scoope
128
     *
129
     * @param string|string[] $config
130
     */
131
    public function load($config, ?string $file = null, ?Schema $schema = null, bool $allow_empty = false)
132
    {
133
        if (is_array($config)) {
134
            foreach ($config as $key => $value) {
135
                if (! is_string($value) || empty($value)) {
136
                    continue;
137
                }
138
                if (is_string($key)) {
139
                    $file = $value;
140
                    $conf = $key;
141
                } else {
142 2
                    $file = null;
143 2
                    $conf = $value;
144
                }
145 2
                self::load($conf, $file, null, $allow_empty);
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

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