Passed
Push — master ( 535bb2...af76a1 )
by Alexis
02:02
created

ConfigurationLoader::loadFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 17
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\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
        if ($this->options->areParametersEnabled()) {
119
            if (isset($configuration[$this->options->getParametersKey()])) {
120
                $this->mergeParameters($configuration[$this->options->getParametersKey()]);
121
            }
122
123
            $this->parseParameters($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
     * Loads file imports recursively.
202
     *
203
     * @param array  $values
204
     * @param string $directory
205
     */
206
    protected function loadImports(&$values, $directory)
207
    {
208
        if (isset($values[$this->options->getImportsKey()])) {
209
            $imports = $values[$this->options->getImportsKey()];
210
            if (is_string($imports)) {
211
                $this->parseFile($directory.DIRECTORY_SEPARATOR.$imports);
212
            } elseif (is_array($imports)) {
213
                foreach ($imports as $key => $file) {
214
                    $this->parseFile($directory.DIRECTORY_SEPARATOR.$file, is_string($key) ? $key : null);
215
                }
216
            }
217
        }
218
219
        unset($values[$this->options->getImportsKey()]);
220
    }
221
222
    /**
223
     * Merges all loaded configurations into a single array.
224
     *
225
     * @return array
226
     */
227
    protected function mergeConfiguration()
228
    {
229
        if (count($this->configurations) > 1) {
230
            return call_user_func_array('array_replace_recursive', $this->configurations);
231
        }
232
233
        return $this->configurations[0];
234
    }
235
236
    /**
237
     * Merges new parameters with existing ones.
238
     *
239
     * @param array $parameters
240
     */
241
    protected function mergeParameters(array $parameters)
242
    {
243
        $this->parameters = array_replace_recursive($this->parameters, $parameters);
244
    }
245
246
    /**
247
     * Parses a configuration file.
248
     *
249
     * @param string $file
250
     * @param string $key
251
     */
252
    protected function parseFile($file, $key = null)
253
    {
254
        $values = $this->loader->load($file);
255
256
        if (!empty($values)) {
257
            if ($this->options->areImportsEnabled()) {
258
                $this->loadImports($values, dirname($file));
259
            }
260
261
            $this->configurations[] = null !== $key ? [$key => $values] : $values;
262
            $this->resources[] = new FileResource($file);
263
        }
264
    }
265
266
    /**
267
     * Parses the configuration and replaces placeholders with parameters values.
268
     *
269
     * @param array $configuration
270
     */
271
    protected function parseParameters(array &$configuration)
272
    {
273
        array_walk_recursive($configuration, function (&$item) {
274
            if (is_string($item)) {
275
                $item = preg_replace_callback('/%([0-9A-Za-z._-]+)%/', function ($matches) {
276
                    return isset($this->parameters[$matches[1]]) ? $this->parameters[$matches[1]] : null;
277
                }, $item);
278
            }
279
        });
280
    }
281
282
    /**
283
     * Includes a PHP file.
284
     *
285
     * @param string $file
286
     *
287
     * @return array
288
     */
289
    private static function requireFile($file)
290
    {
291
        return require $file;
292
    }
293
}
294