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

Config   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Test Coverage

Coverage 44.44%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 123
c 3
b 0
f 0
dl 0
loc 319
ccs 36
cts 81
cp 0.4444
rs 5.04
wmc 57

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A exists() 0 10 2
A set() 0 6 1
A initializeURL() 0 9 3
A missing() 0 3 1
A path() 0 15 4
A has() 0 3 1
A ghost() 0 3 1
B initializeEnvironment() 0 33 7
A get() 0 13 3
B loadRegistrar() 0 26 7
A initializeDebugbar() 0 10 3
A schema() 0 13 3
C load() 0 35 15
A initialize() 0 17 2
A exceptBadConfigValue() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like Config often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Config, and based on these observations, apply Extract Interface, too.

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