generated from yiisoft/package-template
/
Config.php
196 lines (167 loc) · 5.95 KB
/
Config.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<?php
declare(strict_types=1);
namespace Yiisoft\Config;
use ErrorException;
use Yiisoft\Config\Composer\Options;
use function extract;
use function func_get_arg;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function substr;
/**
* Config takes merge plan prepared by {@see \Yiisoft\Config\Composer\EventHandler}
* and executes actual merge for the config group specified.
*/
final class Config implements ConfigInterface
{
private Merger $merger;
private FilesExtractor $filesExtractor;
private bool $isBuildingParams = false;
/**
* @psalm-var array<string, array>
*/
private array $build = [];
/**
* @param ConfigPaths $paths The config paths instance.
* @param string|null $environment The environment name.
* @param object[] $modifiers Modifiers that affect merge process.
* @param string|null $paramsGroup Group name for `$params`. If it is `null`, then `$params` will be empty array.
* @param string $mergePlanFile The merge plan filepath.
*
* @throws ErrorException If the environment does not exist.
*/
public function __construct(
ConfigPaths $paths,
string $environment = null,
array $modifiers = [],
private ?string $paramsGroup = 'params',
string $mergePlanFile = Options::DEFAULT_MERGE_PLAN_FILE,
) {
$environment = empty($environment) ? Options::DEFAULT_ENVIRONMENT : $environment;
/** @psalm-suppress UnresolvableInclude, MixedArgument */
$mergePlan = new MergePlan(require $paths->absolute($mergePlanFile));
if (!$mergePlan->hasEnvironment($environment)) {
$this->throwException(sprintf('The "%s" configuration environment does not exist.', $environment));
}
$dataModifiers = new DataModifiers($modifiers);
$this->merger = new Merger($paths, $dataModifiers);
$this->filesExtractor = new FilesExtractor($paths, $mergePlan, $dataModifiers, $environment);
}
/**
* {@inheritDoc}
*
* @throws ErrorException If the group does not exist or an error occurred during the build.
*/
public function get(string $group): array
{
if (isset($this->build[$group])) {
return $this->build[$group];
}
$this->runBuildParams();
$this->merger->reset();
$this->build[$group] = $this->buildGroup($group);
return $this->build[$group];
}
public function has(string $group): bool
{
return $this->filesExtractor->hasGroup($group);
}
/**
* @throws ErrorException If an error occurred during the build.
*/
private function runBuildParams(): void
{
if ($this->paramsGroup !== null && !isset($this->build[$this->paramsGroup])) {
$this->isBuildingParams = true;
$this->build[$this->paramsGroup] = $this->buildGroup($this->paramsGroup);
$this->isBuildingParams = false;
}
}
/**
* Builds the configuration of the group.
*
* @param string $group The group name.
*
* @throws ErrorException If an error occurred during the build.
*/
private function buildGroup(string $group, array $result = [], ?string $originalGroup = null): array
{
foreach ($this->filesExtractor->extract($group) as $file => $context) {
if ($context->isVariable()) {
$variable = $this->prepareVariable($file, $group);
$result = $this->buildGroup($variable, $result, $originalGroup ?? $group);
} else {
$result = $this->merger->merge(
$context->setOriginalGroup($originalGroup ?? $group),
$result,
$this->buildFile($file),
);
}
}
return $result;
}
/**
* Checks the configuration variable and returns its name.
*
* @param string $variable The variable.
* @param string $group The group name.
*
* @throws ErrorException If the variable name is not valid.
*
* @return string The variable name.
*/
private function prepareVariable(string $variable, string $group): string
{
$name = substr($variable, 1);
if ($name === $group) {
$this->throwException(sprintf(
'The variable "%s" must not be located inside the "%s" config group.',
"$variable",
"$name",
));
}
return $name;
}
/**
* Builds the configuration from the file.
*
* @param string $filePath The file path.
*
* @throws ErrorException If an error occurred during the build.
*
* @return array The configuration from the file.
*/
private function buildFile(string $filePath): array
{
$scopeRequire = static function (): array {
/** @psalm-suppress InvalidArgument, MissingClosureParamType */
set_error_handler(static function (int $errorNumber, string $errorString, string $errorFile, int $errorLine) {
throw new ErrorException($errorString, $errorNumber, 0, $errorFile, $errorLine);
});
/** @psalm-suppress MixedArgument */
extract(func_get_arg(1), EXTR_SKIP);
/**
* @psalm-suppress UnresolvableInclude
* @psalm-var array
*/
$result = require func_get_arg(0);
restore_error_handler();
return $result;
};
$scope = [];
if (!$this->isBuildingParams) {
$scope['config'] = $this;
$scope['params'] = $this->paramsGroup === null ? [] : $this->build[$this->paramsGroup];
}
/** @psalm-suppress TooManyArguments */
return $scopeRequire($filePath, $scope);
}
/**
* @throws ErrorException
*/
private function throwException(string $message): void
{
throw new ErrorException($message, 0, E_USER_ERROR);
}
}