Completed
Push — master ( d7ccd4...e440d3 )
by Andre
01:55
created

Space::processValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
1
<?php
2
3
namespace TheIconic\Config;
4
5
use TheIconic\Config\Exception\ParserException;
6
use TheIconic\Config\Exception\PreconditionException;
7
use TheIconic\Config\Parser\AbstractParser;
8
use TheIconic\Config\Parser\Autodetect as Parser;
9
10
/**
11
 * handler for shared application config
12
 *
13
 * @package Shared\Helper
14
 */
15
class Space
16
{
17
    /**
18
     * @var Config
19
     */
20
    protected $config;
21
22
    /**
23
     * @var array the config file paths
24
     */
25
    protected $paths = [];
26
27
    /**
28
     * @var Cache the config cache handler
29
     */
30
    protected $cache;
31
32
    /**
33
     * @var string
34
     */
35
    protected $cachePath;
36
37
    /**
38
     * @var array the sections to use
39
     */
40
    protected $sections = [];
41
42
    /**
43
     * @var AbstractParser $parser
44
     */
45
    protected $parser;
46
47
    /**
48
     * @var string the config namespace
49
     */
50
    protected $name;
51
52
    /**
53
     * @var array placeholders
54
     */
55
    protected $placeholders = [];
56
57
    /**
58
     * Space constructor.
59
     * @param $name
60
     */
61
    public function __construct($name)
62
    {
63
        $this->name = $name;
64
    }
65
66
    /**
67
     * @param null $key
68
     * @param null $default
69
     * @return array|mixed|null
70
     */
71
    public function get($key = null, $default = null)
72
    {
73
        $this->init();
74
75
        return $this->config->get($key, $default);
76
    }
77
78
    /**
79
     * @return array
80
     */
81
    public function flatten()
82
    {
83
        $this->init();
84
85
        return $this->config->flatten();
86
    }
87
88
    /**
89
     * initialize the config space
90
     *
91
     * loads the config from cache or the config files
92
     */
93
    protected function init()
94
    {
95
        if (null !== $this->config) {
96
            return;
97
        }
98
99
        $cache = $this->getCache();
100
        $cacheKey = $this->getCacheKey();
101
102
        if ($cache->isValid($cacheKey, $this->getTimestamp())) {
103
            $config = $cache->read($cacheKey);
104
        } else {
105
            $config = $this->parse();
106
            $cache->write($cacheKey, $config, $this->getPaths());
107
        }
108
109
        $this->config = new Config($config);
110
    }
111
112
    /**
113
     * parses the configs
114
     *
115
     * @return array the parsed and flattened config array
116
     */
117
    protected function parse()
118
    {
119
        $parser = $this->getParser();
120
121
        $config = [];
122
        foreach ($this->getReadableSources() as $path) {
123
            try {
124
                $config = $this->merge($config, $this->replacePlaceholders($parser->parse($path)));
125
            } catch (ParserException $e) {
126
                // ignore parse errors
127
            }
128
        }
129
130
        return $this->flattenSections($config);
131
    }
132
133
    /**
134
     * @param array $config
135
     * @return array
136
     */
137
    protected function replacePlaceholders(array $config)
138
    {
139
        if (empty($this->placeholders)) {
140
            return $config;
141
        }
142
143
        foreach ($config as $key => $value) {
144
            if (is_array($value)) {
145
                $config[$key] = $this->replacePlaceholders($value);
146
                continue;
147
            }
148
149
            if (is_string($value)) {
150
                $config[$key] = strtr($value, $this->placeholders);
151
            }
152
        }
153
154
        return $config;
155
    }
156
157
    /**
158
     * @return int
159
     */
160
    protected function getTimestamp()
161
    {
162
        $timestamp = 0;
163
        foreach ($this->getReadableSources() as $path) {
164
            $timestamp = max($timestamp, filemtime($path));
165
            $timestamp = max($timestamp, filemtime(dirname($path)));
166
        }
167
168
        return $timestamp;
169
    }
170
    
171
    /**
172
     * get the actually readable source paths
173
     *
174
     * @return array
175
     */
176
    protected function getReadableSources()
177
    {
178
        $paths = [];
179
180
        foreach ($this->getPaths() as $path) {
181
            if (is_readable($path)) {
182
                $paths[] = $path;
183
            }
184
        }
185
186
        return $paths;
187
    }
188
189
    /**
190
     * flattens the config array for the given environment
191
     *
192
     * @param array $config the raw config array
193
     * @return array the flattened config array
194
     */
195
    protected function flattenSections(array $config)
196
    {
197
        $merged = [];
198
199
        $sections = $this->getSections();
200
201
        if (empty($sections)) {
202
            throw new PreconditionException('No sections have been configured.');
203
        }
204
205
        foreach (array_unique($this->getSections()) as $section) {
206
            $section = strtolower($section);
207
208
            if (!isset($config[$section])) {
209
                continue;
210
            }
211
212
            $merged = $this->merge($merged, $config[$section]);
213
        }
214
215
        return $merged;
216
    }
217
    
218
    /**
219
     * get the config cache handler
220
     *
221
     * @return Cache
222
     */
223
    public function getCache()
224
    {
225
        return $this->cache;
226
    }
227
228
    /**
229
     * set the config cache handler
230
     *
231
     * @param Cache $cache
232
     * @return $this
233
     */
234
    public function setCache(Cache $cache)
235
    {
236
        $this->cache = $cache;
237
238
        return $this;
239
    }
240
241
    /**
242
     * get the environment
243
     *
244
     * @return array
245
     */
246
    public function getSections()
247
    {
248
        return $this->sections;
249
    }
250
251
    /**
252
     * set the sections to use
253
     *
254
     * @param array $sections
255
     * @return $this
256
     */
257
    public function setSections(array $sections)
258
    {
259
        $this->sections = $sections;
260
261
        return $this;
262
    }
263
264
    /**
265
     * @param string $section
266
     * @return $this
267
     */
268
    public function addSection(string $section)
269
    {
270
        if (!in_array($section, $this->sections)) {
271
            $this->sections[] = $section;
272
        }
273
274
        return $this;
275
    }
276
277
    /**
278
     * get the parser
279
     *
280
     * @return Parser
281
     */
282
    public function getParser()
283
    {
284
        if (null === $this->parser) {
285
            $this->parser = new Parser();
286
        }
287
288
        return $this->parser;
289
    }
290
291
    /**
292
     * set the parser
293
     *
294
     * @param AbstractParser $parser
295
     * @return $this
296
     */
297
    public function setParser(AbstractParser $parser)
298
    {
299
        $this->parser = $parser;
300
301
        return $this;
302
    }
303
304
    /**
305
     * get configuration file paths
306
     *
307
     * @return array an array of configuration file paths
308
     */
309
    public function getPaths(): array
310
    {
311
        return $this->paths;
312
    }
313
314
    /**
315
     * @param array $paths
316
     * @return Space
317
     */
318
    public function setPaths(array $paths): Space
319
    {
320
        $this->paths = $paths;
321
322
        return $this;
323
    }
324
325
    /**
326
     * @param string $path
327
     * @return Space
328
     */
329
    public function addPath($path): Space
330
    {
331
        if (!in_array($path, $this->paths)) {
332
            $this->paths[] = $path;
333
        }
334
335
        return $this;
336
    }
337
338
    /**
339
     * get the path to the cache config file for the current environment
340
     *
341
     * @return string the file path
342
     */
343
    protected function getCacheKey()
344
    {
345
        return strtolower(md5(sprintf('%s_%s', implode('::', $this->getPaths()), implode('::', $this->getSections()))));
346
    }
347
348
    /**
349
     * recursively merges the arrays
350
     * later keys overload earlier keys
351
     * numeric keys are appended
352
     *
353
     * @param array $base
354
     * @param array $subject
355
     *
356
     * @return array the result of the merge
357
     */
358
    protected function merge(array $base, array $subject): array
359
    {
360
        foreach ($subject as $k => $v) {
361
            if (is_numeric($k)) {
362
                $base[] = $v;
363
            } elseif (array_key_exists($k, $base) && (is_array($v) || is_array($base[$k]))) {
364
                $base[$k] = $this->merge((array) $base[$k], (array) $v);
365
            } else {
366
                $base[$k] = $v;
367
            }
368
        }
369
370
        return $base;
371
    }
372
373
    /**
374
     * @return array
375
     */
376
    public function getPlaceholders()
377
    {
378
        return $this->placeholders;
379
    }
380
381
    /**
382
     * @param array $placeholders
383
     * @return $this
384
     */
385
    public function setPlaceholders(array $placeholders)
386
    {
387
        $this->placeholders = $placeholders;
388
389
        return $this;
390
    }
391
392
    /**
393
     * @param $placeholder
394
     * @param $value
395
     */
396
    public function addPlaceholder($placeholder, $value)
397
    {
398
        $this->placeholders[$placeholder] = $value;
399
    }
400
}
401