Test Failed
Pull Request — main (#59)
by Dimitri
06:14
created

Configurator::build()   A

Complexity

Conditions 2
Paths 6

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
cc 2
eloc 8
c 0
b 0
f 0
nc 6
nop 0
dl 0
loc 12
ccs 5
cts 6
cp 0.8333
crap 2.0185
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\Exceptions\ConfigException;
15
use BlitzPHP\Exceptions\UnknownOptionException;
16
use Dflydev\DotAccessData\Data;
17
use Dflydev\DotAccessData\Exception\DataException;
18
use Dflydev\DotAccessData\Exception\InvalidPathException;
19
use Dflydev\DotAccessData\Exception\MissingPathException;
20
use Nette\Schema\Expect;
21
use Nette\Schema\Processor;
22
use Nette\Schema\Schema;
23
use Nette\Schema\ValidationException;
24
use stdClass;
25
26
/**
27
 * @credit league/config (c) Colin O'Dell <[email protected]>
28
 */
29
final class Configurator
30
{
31
    /**
32
     * @psalm-readonly
33
     */
34
    private readonly Data $userConfig;
35
36
    /**
37
     * @psalm-allow-private-mutation
38
     */
39
    private ?Data $finalConfig = null;
40
41
    /**
42
     * @var array<string, mixed>
43
     *
44
     * @psalm-allow-private-mutation
45
     */
46
    private array $cache = [];
47
48
    /**
49
     * @param array<string, Schema> $configSchemas
50
     */
51
    public function __construct(private array $configSchemas = [])
52
    {
53
        $this->userConfig = new Data();
0 ignored issues
show
Bug introduced by
The property userConfig is declared read-only in BlitzPHP\Config\Configurator.
Loading history...
54
    }
55
56
    /**
57
     * Enregistre un nouveau schéma de configuration à la clé de niveau supérieur donnée
58
     *
59
     * @psalm-allow-private-mutation
60
     */
61
    public function addSchema(string $key, Schema $schema, bool $overwrite = true): void
62
    {
63 2
        $this->invalidate();
64
65
        if ($overwrite || ! isset($this->configSchemas[$key])) {
66 2
            $this->configSchemas[$key] = $schema;
67
        }
68
    }
69
70
    /**
71
     * @psalm-allow-private-mutation
72
     */
73
    public function merge(array $config = []): void
74
    {
75 2
        $this->invalidate();
76
77 2
        $this->userConfig->import($config, Data::REPLACE);
78
    }
79
80
    /**
81
     * @psalm-allow-private-mutation
82
     *
83
     * @param mixed $value
84
     */
85
    public function set(string $key, $value): void
86
    {
87
        $this->invalidate();
88
89
        try {
90
            $this->userConfig->set($key, $value);
91
        } catch (DataException $ex) {
92
            throw new UnknownOptionException($ex->getMessage(), $key, (int) $ex->getCode(), $ex);
93
        }
94
    }
95
96
    /**
97
     * @psalm-external-mutation-free
98
     */
99
    public function get(string $key)
100
    {
101
        if ($this->finalConfig === null) {
102 2
            $this->finalConfig = $this->build();
103
        } elseif (\array_key_exists($key, $this->cache)) {
104 12
            return $this->cache[$key];
105
        }
106
107
        try {
108 6
            return $this->cache[$key] = $this->finalConfig->get($key);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
            return $this->cache[$key] = $this->finalConfig->/** @scrutinizer ignore-call */ get($key);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
109
        } catch (InvalidPathException|MissingPathException $ex) {
110
            throw new UnknownOptionException($ex->getMessage(), $key, (int) $ex->getCode(), $ex);
111
        }
112
    }
113
114
    /**
115
     * @psalm-external-mutation-free
116
     */
117
    public function exists(string $key): bool
118
    {
119
        if ($this->finalConfig === null) {
120 14
            $this->finalConfig = $this->build();
121
        } elseif (\array_key_exists($key, $this->cache)) {
122 12
            return true;
123
        }
124
125
        try {
126 6
            return $this->finalConfig->has($key);
127
        } catch (InvalidPathException) {
128
            return false;
129
        }
130
    }
131
132
    /**
133
     * @psalm-external-mutation-free
134
     */
135
    private function invalidate(): void
136
    {
137 2
        $this->cache       = [];
138 2
        $this->finalConfig = null;
139
    }
140
141
    /**
142
     * Applique le schéma à la configuration pour renvoyer la configuration finale
143
     *
144
     * @throws ValidationException
145
     *
146
     * @psalm-allow-private-mutation
147
     */
148
    private function build(): Data
149
    {
150
        try {
151 2
            $schema    = Expect::structure($this->configSchemas);
152 2
            $processor = new Processor();
153 2
            $processed = $processor->process($schema, $this->userConfig->export());
154
155 2
            $this->raiseAnyDeprecationNotices($processor->getWarnings());
156
157 2
            return $this->finalConfig = new Data(self::convertStdClassesToArrays($processed));
158
        } catch (ValidationException $ex) {
159
            throw new ConfigException($ex->getMessage(), $ex->getCode());
160
        }
161
    }
162
163
    /**
164
     * Convertit récursivement les instances stdClass en tableaux
165
     *
166
     * @param mixed $data
167
     *
168
     * @return mixed
169
     *
170
     * @psalm-pure
171
     */
172
    private static function convertStdClassesToArrays($data)
173
    {
174
        if ($data instanceof stdClass) {
175 2
            $data = (array) $data;
176
        }
177
178
        if (\is_array($data)) {
179
            foreach ($data as $k => $v) {
180 2
                $data[$k] = self::convertStdClassesToArrays($v);
181
            }
182
        }
183
184 2
        return $data;
185
    }
186
187
    /**
188
     * @param list<string> $warnings
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Config\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
189
     */
190
    private function raiseAnyDeprecationNotices(array $warnings): void
191
    {
192
        foreach ($warnings as $warning) {
193 2
            @\trigger_error($warning, \E_USER_DEPRECATED);
194
        }
195
    }
196
}
197