|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the awurth/config package. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) Alexis Wurth <[email protected]> |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
9
|
|
|
* file that was distributed with this source code. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace AWurth\Config; |
|
13
|
|
|
|
|
14
|
|
|
use AWurth\Config\Loader\JsonFileLoader; |
|
15
|
|
|
use AWurth\Config\Loader\PhpFileLoader; |
|
16
|
|
|
use AWurth\Config\Loader\YamlFileLoader; |
|
17
|
|
|
use Symfony\Component\Config\ConfigCache; |
|
18
|
|
|
use Symfony\Component\Config\FileLocator; |
|
19
|
|
|
use Symfony\Component\Config\Loader\DelegatingLoader; |
|
20
|
|
|
use Symfony\Component\Config\Loader\LoaderInterface; |
|
21
|
|
|
use Symfony\Component\Config\Loader\LoaderResolver; |
|
22
|
|
|
use Symfony\Component\Config\Resource\FileResource; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Configuration Loader. |
|
26
|
|
|
* |
|
27
|
|
|
* @author Alexis Wurth <[email protected]> |
|
28
|
|
|
*/ |
|
29
|
|
|
class ConfigurationLoader |
|
30
|
|
|
{ |
|
31
|
|
|
/** |
|
32
|
|
|
* @var array |
|
33
|
|
|
*/ |
|
34
|
|
|
protected $configurations; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* @var LoaderInterface |
|
38
|
|
|
*/ |
|
39
|
|
|
protected $loader; |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @var Options |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $options; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @var array |
|
48
|
|
|
*/ |
|
49
|
|
|
protected $parameters; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* @var FileResource[] |
|
53
|
|
|
*/ |
|
54
|
|
|
protected $resources; |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* Constructor. |
|
58
|
|
|
* |
|
59
|
|
|
* @param Options|array $options |
|
60
|
|
|
* @param array $parameters |
|
61
|
|
|
*/ |
|
62
|
|
|
public function __construct($options = [], array $parameters = []) |
|
63
|
|
|
{ |
|
64
|
|
|
$this->configurations = []; |
|
65
|
|
|
$this->resources = []; |
|
66
|
|
|
$this->parameters = $parameters; |
|
67
|
|
|
|
|
68
|
|
|
if ($options instanceof Options) { |
|
69
|
|
|
$this->options = $options; |
|
70
|
|
|
} elseif (is_array($options)) { |
|
71
|
|
|
$this->options = new Options($options); |
|
72
|
|
|
} else { |
|
73
|
|
|
$this->options = new Options(); |
|
74
|
|
|
} |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* Loads the configuration from a cache file if it exists, or parses a configuration file if not. |
|
79
|
|
|
* |
|
80
|
|
|
* @param string $file |
|
81
|
|
|
* @param string $cachePath |
|
82
|
|
|
* @param bool $debug |
|
83
|
|
|
* |
|
84
|
|
|
* @return array |
|
85
|
|
|
*/ |
|
86
|
|
|
public function load($file, $cachePath = null, $debug = false) |
|
87
|
|
|
{ |
|
88
|
|
|
if (null !== $cachePath) { |
|
89
|
|
|
$cache = new ConfigCache($cachePath, $debug); |
|
90
|
|
|
if (!$cache->isFresh()) { |
|
91
|
|
|
$configuration = $this->loadFile($file); |
|
92
|
|
|
$this->export($cache, $configuration); |
|
93
|
|
|
|
|
94
|
|
|
return $configuration; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
return self::requireFile($cachePath); |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
|
return $this->loadFile($file); |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* Loads the configuration from a file. |
|
105
|
|
|
* |
|
106
|
|
|
* @param string $file |
|
107
|
|
|
* |
|
108
|
|
|
* @return array |
|
109
|
|
|
*/ |
|
110
|
|
|
public function loadFile($file) |
|
111
|
|
|
{ |
|
112
|
|
|
$this->initLoader(); |
|
113
|
|
|
|
|
114
|
|
|
$this->parseFile($file); |
|
115
|
|
|
|
|
116
|
|
|
$configuration = $this->mergeConfiguration(); |
|
117
|
|
|
|
|
118
|
|
View Code Duplication |
if ($this->options->areParametersEnabled()) { |
|
|
|
|
|
|
119
|
|
|
if (isset($configuration[$this->options->getParametersKey()])) { |
|
120
|
|
|
$this->mergeParameters($configuration[$this->options->getParametersKey()]); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
$this->replacePlaceholders($configuration); |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
|
|
return $configuration; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* Gets the parameters. |
|
131
|
|
|
* |
|
132
|
|
|
* @return array |
|
133
|
|
|
*/ |
|
134
|
|
|
public function getParameters() |
|
135
|
|
|
{ |
|
136
|
|
|
return $this->parameters; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Sets the parameters. |
|
141
|
|
|
* |
|
142
|
|
|
* @param array $parameters |
|
143
|
|
|
*/ |
|
144
|
|
|
public function setParameters(array $parameters) |
|
145
|
|
|
{ |
|
146
|
|
|
$this->parameters = $parameters; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Gets the options. |
|
151
|
|
|
* |
|
152
|
|
|
* @return Options |
|
153
|
|
|
*/ |
|
154
|
|
|
public function getOptions() |
|
155
|
|
|
{ |
|
156
|
|
|
return $this->options; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Sets the options. |
|
161
|
|
|
* |
|
162
|
|
|
* @param Options $options |
|
163
|
|
|
*/ |
|
164
|
|
|
public function setOptions(Options $options) |
|
165
|
|
|
{ |
|
166
|
|
|
$this->options = $options; |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
/** |
|
170
|
|
|
* Exports the configuration to a cache file. |
|
171
|
|
|
* |
|
172
|
|
|
* @param ConfigCache $cache |
|
173
|
|
|
* @param array $configuration |
|
174
|
|
|
*/ |
|
175
|
|
|
protected function export(ConfigCache $cache, array $configuration) |
|
176
|
|
|
{ |
|
177
|
|
|
$content = '<?php'.PHP_EOL.PHP_EOL.'return '.var_export($configuration, true).';'.PHP_EOL; |
|
178
|
|
|
|
|
179
|
|
|
$cache->write($content, $this->resources); |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Initializes the file loader. |
|
184
|
|
|
*/ |
|
185
|
|
|
protected function initLoader() |
|
186
|
|
|
{ |
|
187
|
|
|
if (null === $this->loader) { |
|
188
|
|
|
$locator = new FileLocator(); |
|
189
|
|
|
|
|
190
|
|
|
$loaderResolver = new LoaderResolver([ |
|
191
|
|
|
new JsonFileLoader($locator), |
|
192
|
|
|
new PhpFileLoader($locator), |
|
193
|
|
|
new YamlFileLoader($locator) |
|
194
|
|
|
]); |
|
195
|
|
|
|
|
196
|
|
|
$this->loader = new DelegatingLoader($loaderResolver); |
|
197
|
|
|
} |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* Returns whether the file path is an absolute path. |
|
202
|
|
|
* |
|
203
|
|
|
* @param string $file |
|
204
|
|
|
* |
|
205
|
|
|
* @return bool |
|
206
|
|
|
*/ |
|
207
|
|
|
protected function isAbsolutePath($file) |
|
208
|
|
|
{ |
|
209
|
|
|
if ('/' === $file[0] || '\\' === $file[0] |
|
210
|
|
|
|| (strlen($file) > 3 && ctype_alpha($file[0]) |
|
211
|
|
|
&& ':' === $file[1] |
|
212
|
|
|
&& ('\\' === $file[2] || '/' === $file[2]) |
|
213
|
|
|
) |
|
214
|
|
|
|| null !== parse_url($file, PHP_URL_SCHEME) |
|
215
|
|
|
) { |
|
216
|
|
|
return true; |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
return false; |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* Loads an imported file. |
|
224
|
|
|
* |
|
225
|
|
|
* @param string $path |
|
226
|
|
|
* @param string $originalFile |
|
227
|
|
|
* @param string|null $key |
|
228
|
|
|
*/ |
|
229
|
|
|
protected function loadImport($path, $originalFile, $key = null) |
|
230
|
|
|
{ |
|
231
|
|
|
if ($this->options->areParametersEnabled()) { |
|
232
|
|
|
$this->replaceStringPlaceholders($path); |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
if ($this->isAbsolutePath($path) && file_exists($path)) { |
|
236
|
|
|
$this->parseFile($path, $key); |
|
237
|
|
|
} else { |
|
238
|
|
|
$this->parseFile(dirname($originalFile).DIRECTORY_SEPARATOR.$path, $key); |
|
239
|
|
|
} |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* Loads file imports recursively. |
|
244
|
|
|
* |
|
245
|
|
|
* @param array $values |
|
246
|
|
|
* @param string|null $originalFile |
|
247
|
|
|
*/ |
|
248
|
|
|
protected function loadImports(&$values, $originalFile = null) |
|
249
|
|
|
{ |
|
250
|
|
|
if (isset($values[$this->options->getImportsKey()])) { |
|
251
|
|
|
$imports = $values[$this->options->getImportsKey()]; |
|
252
|
|
|
|
|
253
|
|
|
if (is_string($imports)) { |
|
254
|
|
|
$this->loadImport($imports, $originalFile); |
|
255
|
|
|
} elseif (is_array($imports)) { |
|
256
|
|
|
foreach ($imports as $key => $file) { |
|
257
|
|
|
$this->loadImport($file, $originalFile, is_string($key) ? $key : null); |
|
258
|
|
|
} |
|
259
|
|
|
} |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
unset($values[$this->options->getImportsKey()]); |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
/** |
|
266
|
|
|
* Merges all loaded configurations into a single array. |
|
267
|
|
|
* |
|
268
|
|
|
* @return array |
|
269
|
|
|
*/ |
|
270
|
|
|
protected function mergeConfiguration() |
|
271
|
|
|
{ |
|
272
|
|
|
if (count($this->configurations) > 1) { |
|
273
|
|
|
return call_user_func_array('array_replace_recursive', $this->configurations); |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
return $this->configurations[0]; |
|
277
|
|
|
} |
|
278
|
|
|
|
|
279
|
|
|
/** |
|
280
|
|
|
* Merges new parameters with existing ones. |
|
281
|
|
|
* |
|
282
|
|
|
* @param array $parameters |
|
283
|
|
|
*/ |
|
284
|
|
|
protected function mergeParameters(array $parameters) |
|
285
|
|
|
{ |
|
286
|
|
|
$this->parameters = array_replace_recursive($this->parameters, $parameters); |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
/** |
|
290
|
|
|
* Parses a configuration file. |
|
291
|
|
|
* |
|
292
|
|
|
* @param string $file |
|
293
|
|
|
* @param string $key |
|
294
|
|
|
*/ |
|
295
|
|
|
protected function parseFile($file, $key = null) |
|
296
|
|
|
{ |
|
297
|
|
|
$values = $this->loader->load($file); |
|
298
|
|
|
|
|
299
|
|
|
if (!empty($values)) { |
|
300
|
|
View Code Duplication |
if ($this->options->areParametersEnabled() && isset($values[$this->options->getParametersKey()])) { |
|
|
|
|
|
|
301
|
|
|
$this->replacePlaceholders($values[$this->options->getParametersKey()]); |
|
302
|
|
|
$this->mergeParameters($values[$this->options->getParametersKey()]); |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
if ($this->options->areImportsEnabled()) { |
|
306
|
|
|
$this->loadImports($values, $file); |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
$this->configurations[] = null !== $key ? [$key => $values] : $values; |
|
310
|
|
|
$this->resources[] = new FileResource($file); |
|
311
|
|
|
} |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
/** |
|
315
|
|
|
* Parses the configuration and replaces placeholders with the corresponding parameters values. |
|
316
|
|
|
* |
|
317
|
|
|
* @param array $configuration |
|
318
|
|
|
*/ |
|
319
|
|
|
protected function replacePlaceholders(array &$configuration) |
|
320
|
|
|
{ |
|
321
|
|
|
array_walk_recursive($configuration, [$this, 'replaceStringPlaceholders']); |
|
322
|
|
|
} |
|
323
|
|
|
|
|
324
|
|
|
/** |
|
325
|
|
|
* Replaces configuration placeholders with the corresponding parameters values. |
|
326
|
|
|
* |
|
327
|
|
|
* @param string $string |
|
328
|
|
|
*/ |
|
329
|
|
|
protected function replaceStringPlaceholders(&$string) |
|
330
|
|
|
{ |
|
331
|
|
|
if (is_string($string)) { |
|
332
|
|
|
$string = preg_replace_callback('/%([0-9A-Za-z._-]+)%/', function ($matches) { |
|
333
|
|
|
return isset($this->parameters[$matches[1]]) ? $this->parameters[$matches[1]] : null; |
|
334
|
|
|
}, $string); |
|
335
|
|
|
} |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
/** |
|
339
|
|
|
* Includes a PHP file. |
|
340
|
|
|
* |
|
341
|
|
|
* @param string $file |
|
342
|
|
|
* |
|
343
|
|
|
* @return array |
|
344
|
|
|
*/ |
|
345
|
|
|
private static function requireFile($file) |
|
346
|
|
|
{ |
|
347
|
|
|
return require $file; |
|
348
|
|
|
} |
|
349
|
|
|
} |
|
350
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.