Test Failed
Push — main ( e1affe...2e6d72 )
by Dimitri
14:06
created

Config::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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