Passed
Push — main ( 1e579d...c0f50c )
by
unknown
08:04 queued 03:57
created

Config::initialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 17
ccs 0
cts 9
cp 0
crap 6
rs 9.9332
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 4
            $config = explode('.', $key);
60 4
            $this->load($config[0]);
61
62 4
            return $this->configurator->exists(implode('.', $config));
63
        }
64
65 78
        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
        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
        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 80
            return $this->configurator->get($key);
93
        }
94
95
        if (func_num_args() > 1) {
96
            return $default;
97
        }
98
99
        $path = explode('.', $key);
100
101
        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
        $this->configurator->set($key, $value);
112
    }
113
114
    /**
115
     * Charger la configuration spécifique dans le scoope
116
     *
117
     * @param string|string[] $config
118
     */
119
    public function load($config, ?string $file = null, ?Schema $schema = null)
120
    {
121
        if (is_array($config)) {
122
            foreach ($config as $key => $value) {
123
                if (! is_string($value) || empty($value)) {
124
                    continue;
125
                }
126
                if (is_string($key)) {
127
                    $file = $value;
128
                    $conf = $key;
129
                } else {
130
                    $file = null;
131
                    $conf = $value;
132
                }
133
                self::load($conf, $file);
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

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