Completed
Push — fix-config ( 5d6323 )
by Arnaud
01:44
created

Config::import()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.4426
c 0
b 0
f 0
cc 7
nc 6
nop 1
1
<?php
2
/*
3
 * Copyright (c) Arnaud Ligny <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Cecil;
10
11
use Cecil\Exception\Exception;
12
use Dflydev\DotAccessData\Data;
13
14
/**
15
 * Class Config.
16
 */
17
class Config
18
{
19
    /**
20
     * Configuration is a Data object.
21
     *
22
     * @var Data
23
     */
24
    protected $data;
25
    /**
26
     * Local configuration.
27
     *
28
     * @var Config|array|null
29
     */
30
    protected $localConfig;
31
    /**
32
     * Source directory.
33
     *
34
     * @var string
35
     */
36
    protected $sourceDir;
37
    /**
38
     * Destination directory.
39
     *
40
     * @var string
41
     */
42
    protected $destinationDir;
43
44
    /**
45
     * Config constructor.
46
     *
47
     * @param Config|array|null $config
48
     */
49
    public function __construct($config = null)
50
    {
51
        // default config
52
        $this->data = new Data(include __DIR__.'/../config/default.php');
53
        // import local config
54
        $this->localConfig = $config;
55
        $this->import($this->localConfig);
0 ignored issues
show
Bug introduced by
It seems like $this->localConfig can also be of type null; however, Cecil\Config::import() does only seem to accept object<Cecil\Config>|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
56
    }
57
58
    /**
59
     * Import config data into the current configuration.
60
     *
61
     * @param Config|array $config
62
     *
63
     * @return void
64
     */
65
    public function import($config): void
66
    {
67
        if ($config instanceof self) {
68
            $this->data->importData($config->getData());
69
        } elseif (is_array($config)) {
70
            $this->data->import($config);
71
        }
72
        // re-import local config
73
        if ($config !== $this->localConfig) {
74
            $this->import($this->localConfig);
0 ignored issues
show
Bug introduced by
It seems like $this->localConfig can also be of type null; however, Cecil\Config::import() does only seem to accept object<Cecil\Config>|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
75
        }
76
77
        /**
78
         * Apply environment variables.
79
         */
80
        $data = $this->getData();
81
        $applyEnv = function ($array) use ($data) {
82
            $iterator = new \RecursiveIteratorIterator(
83
                new \RecursiveArrayIterator($array),
84
                \RecursiveIteratorIterator::SELF_FIRST
85
            );
86
            foreach ($iterator as $leafValue) {
87
                $path = [];
88
                foreach (range(0, $iterator->getDepth()) as $depth) {
89
                    $path[] = $iterator->getSubIterator($depth)->key();
90
                }
91
                $sPath = implode('_', $path);
92
                if ($getEnv = getenv('CECIL_'.strtoupper($sPath))) {
93
                    $data->set(str_replace('_', '.', strtolower($sPath)), $getEnv);
94
                }
95
            }
96
        };
97
        $applyEnv($data->export());
98
    }
99
100
    /**
101
     * Set a Data object as configuration.
102
     *
103
     * @param Data $data
104
     *
105
     * @return $this
106
     */
107
    protected function setData(Data $data): self
108
    {
109
        if ($this->data !== $data) {
110
            $this->data = $data;
111
        }
112
113
        return $this;
114
    }
115
116
    /**
117
     * Get configuration as a Data object.
118
     *
119
     * @return Data
120
     */
121
    public function getData(): Data
122
    {
123
        return $this->data;
124
    }
125
126
    /**
127
     * Get configuration as an array.
128
     *
129
     * @return array
130
     */
131
    public function getAsArray(): array
132
    {
133
        return $this->data->export();
134
    }
135
136
    /**
137
     * Is configuration's key' exists?
138
     *
139
     * @param string $key
140
     *
141
     * @return bool
142
     */
143
    public function has(string $key): bool
144
    {
145
        return $this->data->has($key);
146
    }
147
148
    /**
149
     * Get the value of a configuration's key'.
150
     *
151
     * @param string      $key
152
     * @param string|null $language
153
     *
154
     * @return array|mixed|null
155
     */
156
    public function get(string $key, string $language = null)
157
    {
158
        if ($language !== null) {
159
            $keyLang = "languages.$language.$key";
160
            if ($this->data->has($keyLang)) {
161
                return $this->data->get($keyLang);
162
            }
163
        }
164
165
        return $this->data->get($key);
166
    }
167
168
    /**
169
     * Set the source directory.
170
     *
171
     * @param string|null $sourceDir
172
     *
173
     * @throws \InvalidArgumentException
174
     *
175
     * @return $this
176
     */
177
    public function setSourceDir(string $sourceDir = null): self
178
    {
179
        if ($sourceDir === null) {
180
            $sourceDir = getcwd();
181
        }
182
        if (!is_dir($sourceDir)) {
183
            throw new \InvalidArgumentException(sprintf('The directory "%s" is not a valid source!', $sourceDir));
184
        }
185
        $this->sourceDir = $sourceDir;
186
187
        return $this;
188
    }
189
190
    /**
191
     * Get the source directory.
192
     *
193
     * @return string
194
     */
195
    public function getSourceDir(): string
196
    {
197
        return $this->sourceDir;
198
    }
199
200
    /**
201
     * Set the destination directory.
202
     *
203
     * @param string|null $destinationDir
204
     *
205
     * @throws \InvalidArgumentException
206
     *
207
     * @return $this
208
     */
209
    public function setDestinationDir(string $destinationDir = null): self
210
    {
211
        if ($destinationDir === null) {
212
            $destinationDir = $this->sourceDir;
213
        }
214
        if (!is_dir($destinationDir)) {
215
            throw new \InvalidArgumentException(sprintf(
216
                'The directory "%s" is not a valid destination!',
217
                $destinationDir
218
            ));
219
        }
220
        $this->destinationDir = $destinationDir;
221
222
        return $this;
223
    }
224
225
    /**
226
     * Get the destination directory.
227
     *
228
     * @return string
229
     */
230
    public function getDestinationDir(): string
231
    {
232
        return $this->destinationDir;
233
    }
234
235
    /**
236
     * Paths helpers.
237
     */
238
239
    /**
240
     * Return the path of the content directory.
241
     *
242
     * @return string
243
     */
244
    public function getContentPath(): string
245
    {
246
        return $this->getSourceDir().'/'.$this->get('content.dir');
247
    }
248
249
    /**
250
     * Return the path of templates directory.
251
     *
252
     * @return string
253
     */
254
    public function getLayoutsPath(): string
255
    {
256
        return $this->getSourceDir().'/'.$this->get('layouts.dir');
257
    }
258
259
    /**
260
     * Return the path of themes directory.
261
     *
262
     * @return string
263
     */
264
    public function getThemesPath(): string
265
    {
266
        return $this->getSourceDir().'/'.$this->get('themes.dir');
267
    }
268
269
    /**
270
     * Return the path of internal templates directory.
271
     *
272
     * @return string
273
     */
274
    public function getInternalLayoutsPath(): string
275
    {
276
        return __DIR__.'/../'.$this->get('layouts.internal.dir');
277
    }
278
279
    /**
280
     * Return the path of the output directory.
281
     *
282
     * @return string
283
     */
284
    public function getOutputPath(): string
285
    {
286
        return $this->getDestinationDir().'/'.$this->get('output.dir');
287
    }
288
289
    /**
290
     * Return the path of static files directory.
291
     *
292
     * @return string
293
     */
294
    public function getStaticPath(): string
295
    {
296
        return $this->getSourceDir().'/'.$this->get('static.dir');
297
    }
298
299
    /**
300
     * Return a "clean" array of an output format.
301
     *
302
     * @param string $format
303
     *
304
     * @return array
305
     */
306
    public function getOutputFormat(string $format): array
307
    {
308
        $default = [
309
            'mediatype' => null, // 'text/html'
310
            'subpath'   => null, // ''
311
            'suffix'    => null, // '/index'
312
            'extension' => null, // 'html'
313
        ];
314
315
        $result = $this->get(sprintf('output.formats.%s', $format));
316
317
        return array_merge($default, $result);
318
    }
319
320
    /**
321
     * Theme helpers.
322
     */
323
324
    /**
325
     * Return theme(s) as an array.
326
     *
327
     * @return array|null
328
     */
329
    public function getTheme(): ?array
330
    {
331
        if ($themes = $this->get('theme')) {
332
            if (is_array($themes)) {
333
                return $themes;
334
            }
335
336
            return [$themes];
337
        }
338
339
        return null;
340
    }
341
342
    /**
343
     * Has a (valid) theme(s)?
344
     *
345
     * @throws Exception
346
     *
347
     * @return bool
348
     */
349
    public function hasTheme(): bool
350
    {
351
        if ($themes = $this->getTheme()) {
352
            foreach ($themes as $theme) {
353
                if (!Util::getFS()->exists($this->getThemeDirPath($theme, 'layouts'))) {
354
                    throw new Exception(sprintf(
355
                        "Theme directory '%s/%s/layouts' not found!",
356
                        $this->getThemesPath(),
357
                        $theme
358
                    ));
359
                }
360
            }
361
362
            return true;
363
        }
364
365
        return false;
366
    }
367
368
    /**
369
     * Return the path of a specific theme's directory.
370
     * ("layouts" by default).
371
     *
372
     * @param string $theme
373
     * @param string $dir
374
     *
375
     * @return string
376
     */
377
    public function getThemeDirPath(string $theme, string $dir = 'layouts'): string
378
    {
379
        return $this->getThemesPath().'/'.$theme.'/'.$dir;
380
    }
381
382
    /**
383
     * Language helpers.
384
     */
385
386
    /**
387
     * Return an array of available languages.
388
     *
389
     * @return array
390
     */
391
    public function getLanguages(): array
392
    {
393
        return $this->get('languages');
394
    }
395
396
    /**
397
     * Return the default language key (ie: "en", "fr-fr", etc.).
398
     *
399
     * @return string
400
     */
401
    public function getLanguageDefaultKey(): string
402
    {
403
        if ($this->get('language')) {
404
            return $this->get('language');
405
        }
406
407
        $languages = $this->getLanguages();
408
        if (!is_array($languages)) {
409
            throw new Exception('There is no default "language" in configuration!');
410
        }
411
        reset($languages);
412
413
        return key($languages);
414
    }
415
416
    /**
417
     * Return properties of a (specified or default) language.
418
     *
419
     * @param string|null $key
420
     *
421
     * @return array
422
     */
423
    public function getLanguageProperties(string $key = null): array
424
    {
425
        $key = $key ?? $this->getLanguageDefaultKey();
426
427
        $languageProperties = $this->get(sprintf('languages.%s', $key));
428
        if (!is_array($languageProperties)) {
429
            throw new Exception(sprintf('Language "%s" is not correctly set in config!', $key));
430
        }
431
432
        return $languageProperties;
433
    }
434
435
    /**
436
     * Return the property value of a (specified or default) language.
437
     *
438
     * @param string      $property
439
     * @param string|null $key
440
     *
441
     * @return string
442
     */
443
    public function getLanguageProperty($property, $key = null): string
444
    {
445
        $properties = ['name', 'locale'];
446
        $languageProperties = $this->getLanguageProperties($key);
447
448
        if (!in_array($property, $properties)) {
449
            throw new Exception(sprintf(
450
                'Property language "%s" is not available!',
451
                $property
452
            ));
453
        }
454
        if (!\array_key_exists($property, $languageProperties)) {
455
            throw new Exception(sprintf(
456
                'Property "%s" is not defined for language "%s"!',
457
                $property,
458
                $languageProperties['name']
459
            ));
460
        }
461
462
        return $languageProperties[$property];
463
    }
464
}
465