ConfigurationLoader::initLoader()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
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\ConfigCacheInterface;
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 ConfigCacheInterface
33
     */
34
    protected $cache;
35
36
    /**
37
     * @var array
38
     */
39
    protected $configurations;
40
41
    /**
42
     * @var LoaderInterface
43
     */
44
    protected $loader;
45
46
    /**
47
     * @var LoaderInterface[]
48
     */
49
    protected $loaders;
50
51
    /**
52
     * @var Options
53
     */
54
    protected $options;
55
56
    /**
57
     * @var array
58
     */
59
    protected $parameters;
60
61
    /**
62
     * @var FileResource[]
63
     */
64
    protected $resources;
65
66
    /**
67
     * Constructor.
68
     *
69
     * @param string $cachePath
70
     * @param bool   $debug
71
     */
72
    public function __construct($cachePath = null, $debug = false)
73
    {
74
        $this->configurations = [];
75
        $this->resources = [];
76
        $this->parameters = [];
77
        $this->options = new Options();
78
79
        if (null !== $cachePath) {
80
            $this->cache = new ConfigCache($cachePath, $debug);
81
        }
82
    }
83
84
    /**
85
     * Loads the configuration from a cache file if it exists, or parses a configuration file if not.
86
     *
87
     * @param string $file
88
     *
89
     * @return array
90
     */
91
    public function load($file)
92
    {
93
        if (null !== $this->cache) {
94
            if (!$this->cache->isFresh()) {
95
                $configuration = $this->loadFile($file);
96
                $this->export($configuration);
97
98
                return $configuration;
99
            }
100
101
            return self::requireFile($this->cache->getPath());
102
        }
103
104
        return $this->loadFile($file);
105
    }
106
107
    /**
108
     * Loads the configuration from a file.
109
     *
110
     * @param string $file
111
     *
112
     * @return array
113
     */
114
    public function loadFile($file)
115
    {
116
        $this->initLoader();
117
118
        $this->parseFile($file);
119
120
        $configuration = $this->mergeConfiguration();
121
122 View Code Duplication
        if ($this->options->areParametersEnabled()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
123
            if (isset($configuration[$this->options->getParametersKey()])) {
124
                $this->mergeParameters($configuration[$this->options->getParametersKey()]);
125
            }
126
127
            $this->replacePlaceholders($configuration);
128
        }
129
130
        return $configuration;
131
    }
132
133
    /**
134
     * Exports the configuration to a cache file.
135
     *
136
     * @param array $configuration
137
     */
138
    public function export(array $configuration)
139
    {
140
        $content = '<?php'.PHP_EOL.PHP_EOL.'return '.var_export($configuration, true).';'.PHP_EOL;
141
142
        $this->cache->write($content, $this->resources);
143
    }
144
145
    /**
146
     * Gets the configuration cache.
147
     *
148
     * @return ConfigCacheInterface
149
     */
150
    public function getCache()
151
    {
152
        return $this->cache;
153
    }
154
155
    /**
156
     * Sets the configuration cache.
157
     *
158
     * @param ConfigCacheInterface $cache
159
     */
160
    public function setCache(ConfigCacheInterface $cache)
161
    {
162
        $this->cache = $cache;
163
    }
164
165
    /**
166
     * Gets the file loaders.
167
     *
168
     * @return LoaderInterface[]
169
     */
170
    public function getLoaders()
171
    {
172
        return $this->loaders;
173
    }
174
175
    /**
176
     * Adds a file loader.
177
     *
178
     * @param LoaderInterface $loader
179
     *
180
     * @return self
181
     */
182
    public function addLoader(LoaderInterface $loader)
183
    {
184
        $this->loaders[] = $loader;
185
186
        return $this;
187
    }
188
189
    /**
190
     * Sets the file loaders.
191
     *
192
     * @param LoaderInterface[] $loaders
193
     */
194
    public function setLoaders(array $loaders)
195
    {
196
        $this->loaders = $loaders;
197
    }
198
199
    /**
200
     * Gets the parameters.
201
     *
202
     * @return array
203
     */
204
    public function getParameters()
205
    {
206
        return $this->parameters;
207
    }
208
209
    /**
210
     * Sets a parameter's value.
211
     *
212
     * @param string $name
213
     * @param mixed  $value
214
     *
215
     * @return self
216
     */
217
    public function setParameter($name, $value)
218
    {
219
        $this->parameters[$name] = $value;
220
221
        return $this;
222
    }
223
224
    /**
225
     * Sets the parameters.
226
     *
227
     * @param array $parameters
228
     */
229
    public function setParameters(array $parameters)
230
    {
231
        $this->parameters = $parameters;
232
    }
233
234
    /**
235
     * Gets the options.
236
     *
237
     * @return Options
238
     */
239
    public function getOptions()
240
    {
241
        return $this->options;
242
    }
243
244
    /**
245
     * Sets the options.
246
     *
247
     * @param Options $options
248
     */
249
    public function setOptions(Options $options)
250
    {
251
        $this->options = $options;
252
    }
253
254
    /**
255
     * Initializes the file loader.
256
     */
257
    protected function initLoader()
258
    {
259
        if (null === $this->loader) {
260
            $this->addLoader(new PhpFileLoader());
261
            $this->addLoader(new YamlFileLoader());
262
            $this->addLoader(new JsonFileLoader());
263
264
            $this->loader = new DelegatingLoader(new LoaderResolver($this->loaders));
265
        }
266
    }
267
268
    /**
269
     * Returns whether the file path is an absolute path.
270
     *
271
     * @param string $file
272
     *
273
     * @return bool
274
     */
275
    protected function isAbsolutePath($file)
276
    {
277
        if ('/' === $file[0] || '\\' === $file[0]
278
            || (strlen($file) > 3 && ctype_alpha($file[0])
279
                && ':' === $file[1]
280
                && ('\\' === $file[2] || '/' === $file[2])
281
            )
282
            || null !== parse_url($file, PHP_URL_SCHEME)
283
        ) {
284
            return true;
285
        }
286
287
        return false;
288
    }
289
290
    /**
291
     * Loads an imported file.
292
     *
293
     * @param string      $path
294
     * @param string      $originalFile
295
     * @param string|null $key
296
     */
297
    protected function loadImport($path, $originalFile, $key = null)
298
    {
299
        if ($this->options->areParametersEnabled()) {
300
            $this->replaceStringPlaceholders($path);
301
        }
302
303
        if ($this->isAbsolutePath($path) && file_exists($path)) {
304
            $this->parseFile($path, $key);
305
        } else {
306
            $this->parseFile(dirname($originalFile).DIRECTORY_SEPARATOR.$path, $key);
307
        }
308
    }
309
310
    /**
311
     * Loads file imports recursively.
312
     *
313
     * @param array       $values
314
     * @param string|null $originalFile
315
     */
316
    protected function loadImports(&$values, $originalFile = null)
317
    {
318
        if (isset($values[$this->options->getImportsKey()])) {
319
            $imports = $values[$this->options->getImportsKey()];
320
321
            if (is_string($imports)) {
322
                $this->loadImport($imports, $originalFile);
323
            } elseif (is_array($imports)) {
324
                foreach ($imports as $key => $file) {
325
                    $this->loadImport($file, $originalFile, is_string($key) ? $key : null);
326
                }
327
            }
328
        }
329
330
        unset($values[$this->options->getImportsKey()]);
331
    }
332
333
    /**
334
     * Merges all loaded configurations into a single array.
335
     *
336
     * @return array
337
     */
338
    protected function mergeConfiguration()
339
    {
340
        if (count($this->configurations) > 1) {
341
            return call_user_func_array('array_replace_recursive', $this->configurations);
342
        }
343
344
        return $this->configurations[0];
345
    }
346
347
    /**
348
     * Merges new parameters with existing ones.
349
     *
350
     * @param array $parameters
351
     */
352
    protected function mergeParameters(array $parameters)
353
    {
354
        $this->parameters = array_replace_recursive($this->parameters, $parameters);
355
    }
356
357
    /**
358
     * Parses a configuration file.
359
     *
360
     * @param string $file
361
     * @param string $key
362
     */
363
    protected function parseFile($file, $key = null)
364
    {
365
        $values = $this->loader->load($file);
366
367
        if (!empty($values)) {
368 View Code Duplication
            if ($this->options->areParametersEnabled() && isset($values[$this->options->getParametersKey()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
369
                $this->replacePlaceholders($values[$this->options->getParametersKey()]);
370
                $this->mergeParameters($values[$this->options->getParametersKey()]);
371
            }
372
373
            if ($this->options->areImportsEnabled()) {
374
                $this->loadImports($values, $file);
375
            }
376
377
            $this->configurations[] = null !== $key ? [$key => $values] : $values;
378
            $this->resources[] = new FileResource($file);
379
        }
380
    }
381
382
    /**
383
     * Parses the configuration and replaces placeholders with the corresponding parameters values.
384
     *
385
     * @param array $configuration
386
     */
387
    protected function replacePlaceholders(array &$configuration)
388
    {
389
        array_walk_recursive($configuration, [$this, 'replaceStringPlaceholders']);
390
    }
391
392
    /**
393
     * Replaces configuration placeholders with the corresponding parameters values.
394
     *
395
     * @param string $string
396
     */
397
    protected function replaceStringPlaceholders(&$string)
398
    {
399
        if (is_string($string)) {
400
            if (preg_match('/^%([0-9A-Za-z._-]+)%$/', $string, $matches)) {
401
                if (isset($this->parameters[$matches[1]])) {
402
                    $string = $this->parameters[$matches[1]];
403
                }
404
            } else {
405
                $string = preg_replace_callback('/%([0-9A-Za-z._-]+)%/', function ($matches) {
406
                    if (isset($this->parameters[$matches[1]]) && !in_array(gettype($this->parameters[$matches[1]]), ['object', 'array'])) {
407
                        return $this->parameters[$matches[1]];
408
                    } else {
409
                        return $matches[0];
410
                    }
411
                }, $string);
412
            }
413
        }
414
    }
415
416
    /**
417
     * Includes a PHP file.
418
     *
419
     * @param string $file
420
     *
421
     * @return array
422
     */
423
    private static function requireFile($file)
424
    {
425
        return require $file;
426
    }
427
}
428