Passed
Push — master ( 72c53c...f7b5a3 )
by Alexis
01:53
created

ConfigurationLoader::parseParameters()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 3
eloc 5
nc 1
nop 1
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()) {
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...
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()])) {
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...
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