Passed
Push — feat_i18n ( c4f2d5...2092c2 )
by Arnaud
03:41
created

Config::getCacheAssetsPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil;
15
16
use Cecil\Exception\RuntimeException;
17
use Cecil\Util\Plateform;
18
use Dflydev\DotAccessData\Data;
19
20
/**
21
 * Class Config.
22
 */
23
class Config
24
{
25
    /** @var Data Configuration is a Data object. */
26
    protected $data;
27
28
    /** @var array Configuration. */
29
    protected $siteConfig;
30
31
    /** @var string Source directory. */
32
    protected $sourceDir;
33
34
    /** @var string Destination directory. */
35
    protected $destinationDir;
36
37
    /** @var array Languages. */
38
    protected $languages = null;
39
40
    /**
41
     * @param array|null $config
42
     */
43 1
    public function __construct(array $config = null)
44
    {
45
        // load default configuration
46 1
        $defaultConfig = realpath(Util::joinFile(__DIR__, '..', 'config/default.php'));
47 1
        if (Plateform::isPhar()) {
48
            $defaultConfig = Util::joinPath(Plateform::getPharPath(), 'config/default.php');
49
        }
50 1
        $this->data = new Data(include $defaultConfig);
51
52
        // import site config
53 1
        $this->siteConfig = $config;
54 1
        $this->importSiteConfig();
55
    }
56
57
    /**
58
     * Imports site configuration.
59
     */
60 1
    protected function importSiteConfig(): void
61
    {
62 1
        $this->data->import($this->siteConfig);
63
64
        /**
65
         * Overrides configuration with environment variables.
66
         */
67 1
        $data = $this->getData();
68 1
        $applyEnv = function ($array) use ($data) {
69 1
            $iterator = new \RecursiveIteratorIterator(
70 1
                new \RecursiveArrayIterator($array),
71
                \RecursiveIteratorIterator::SELF_FIRST
72
            );
73 1
            $iterator->rewind();
74 1
            while ($iterator->valid()) {
75 1
                $path = [];
76 1
                foreach (range(0, $iterator->getDepth()) as $depth) {
77 1
                    $path[] = $iterator->getSubIterator($depth)->key();
78
                }
79 1
                $sPath = implode('_', $path);
80 1
                if ($getEnv = getenv('CECIL_'.strtoupper($sPath))) {
81 1
                    $data->set(str_replace('_', '.', strtolower($sPath)), $this->castSetValue($getEnv));
82
                }
83 1
                $iterator->next();
84
            }
85
        };
86 1
        $applyEnv($data->export());
87
    }
88
89
    /**
90
     * Casts boolean value given to set() as string.
91
     */
92 1
    private function castSetValue($value)
93
    {
94 1
        if (is_string($value)) {
95 1
            switch ($value) {
96 1
                case 'true':
97 1
                    return true;
98 1
                case 'false':
99
                    return false;
100
                default:
101 1
                    return $value;
102
            }
103
        }
104
105
        return $value;
106
    }
107
108
    /**
109
     * Imports (theme) configuration.
110
     *
111
     * @param array|null $config
112
     */
113 1
    public function import(array $config): void
114
    {
115 1
        $this->data->import($config);
116
117
        // re-import site config
118 1
        $this->importSiteConfig();
119
    }
120
121
    /**
122
     * Set a Data object as configuration.
123
     */
124
    protected function setData(Data $data): self
125
    {
126
        if ($this->data !== $data) {
127
            $this->data = $data;
128
        }
129
130
        return $this;
131
    }
132
133
    /**
134
     * Get configuration as a Data object.
135
     */
136 1
    public function getData(): Data
137
    {
138 1
        return $this->data;
139
    }
140
141
    /**
142
     * Get configuration as an array.
143
     */
144
    public function getAsArray(): array
145
    {
146
        return $this->data->export();
147
    }
148
149
    /**
150
     * Is configuration's key exists?
151
     */
152 1
    public function has(string $key): bool
153
    {
154 1
        return $this->data->has($key);
155
    }
156
157
    /**
158
     * Get the value of a configuration's key.
159
     *
160
     * @return mixed|null
161
     */
162 1
    public function get(string $key, string $language = null, bool $fallback = true)
163
    {
164 1
        if ($language !== null) {
165 1
            $index = $this->getLanguageIndex($language);
166 1
            $keyLang = \sprintf('languages.%s.config.%s', $index, $key);
167 1
            if ($this->data->has($keyLang)) {
168 1
                return $this->data->get($keyLang);
169
            }
170 1
            if ($language !== $this->getLanguageDefault() && $fallback === false) {
171
                return null;
172
            }
173
        }
174
175 1
        if ($this->data->has($key)) {
176 1
            return $this->data->get($key);
177
        }
178
179
        return null;
180
    }
181
182
    /**
183
     * Set the source directory.
184
     *
185
     * @throws \InvalidArgumentException
186
     */
187 1
    public function setSourceDir(string $sourceDir = null): self
188
    {
189 1
        if ($sourceDir === null) {
190 1
            $sourceDir = getcwd();
191
        }
192 1
        if (!is_dir($sourceDir)) {
193
            throw new \InvalidArgumentException(\sprintf('The directory "%s" is not a valid source!', $sourceDir));
194
        }
195 1
        $this->sourceDir = $sourceDir;
196
197 1
        return $this;
198
    }
199
200
    /**
201
     * Get the source directory.
202
     */
203 1
    public function getSourceDir(): string
204
    {
205 1
        return $this->sourceDir;
206
    }
207
208
    /**
209
     * Set the destination directory.
210
     *
211
     * @throws \InvalidArgumentException
212
     */
213 1
    public function setDestinationDir(string $destinationDir = null): self
214
    {
215 1
        if ($destinationDir === null) {
216 1
            $destinationDir = $this->sourceDir;
217
        }
218 1
        if (!is_dir($destinationDir)) {
219
            throw new \InvalidArgumentException(\sprintf(
220
                'The directory "%s" is not a valid destination!',
221
                $destinationDir
222
            ));
223
        }
224 1
        $this->destinationDir = $destinationDir;
225
226 1
        return $this;
227
    }
228
229
    /**
230
     * Get the destination directory.
231
     */
232 1
    public function getDestinationDir(): string
233
    {
234 1
        return $this->destinationDir;
235
    }
236
237
    /**
238
     * Path helpers.
239
     */
240
241
    /**
242
     * Returns the path of the pages directory.
243
     */
244 1
    public function getPagesPath(): string
245
    {
246 1
        $path = Util::joinFile($this->getSourceDir(), (string) $this->get('pages.dir'));
247
248
        // legacy support
249 1
        if (!is_dir($path)) {
250
            $path = Util::joinFile($this->getSourceDir(), 'content');
251
        }
252
253 1
        return $path;
254
    }
255
256
    /**
257
     * Returns the path of the data directory.
258
     */
259 1
    public function getDataPath(): string
260
    {
261 1
        return Util::joinFile($this->getSourceDir(), (string) $this->get('data.dir'));
262
    }
263
264
    /**
265
     * Returns the path of templates directory.
266
     */
267 1
    public function getLayoutsPath(): string
268
    {
269 1
        return Util::joinFile($this->getSourceDir(), (string) $this->get('layouts.dir'));
270
    }
271
272
    /**
273
     * Returns the path of themes directory.
274
     */
275 1
    public function getThemesPath(): string
276
    {
277 1
        return Util::joinFile($this->getSourceDir(), (string) $this->get('themes.dir'));
278
    }
279
280
    /**
281
     * Returns the path of internal templates directory.
282
     */
283 1
    public function getInternalLayoutsPath(): string
284
    {
285 1
        return Util::joinPath(__DIR__, '..', (string) $this->get('layouts.internal.dir'));
286
    }
287
288
    /**
289
     * Returns the path of translations directory.
290
     */
291 1
    public function getTranslationsPath(): string
292
    {
293 1
        return Util::joinFile($this->getSourceDir(), 'translations');
294
    }
295
296
    /**
297
     * Returns the path of the output directory.
298
     */
299 1
    public function getOutputPath(): string
300
    {
301 1
        return Util::joinFile($this->getDestinationDir(), (string) $this->get('output.dir'));
302
    }
303
304
    /**
305
     * Returns the path of static files directory.
306
     */
307 1
    public function getStaticPath(): string
308
    {
309 1
        return Util::joinFile($this->getSourceDir(), (string) $this->get('static.dir'));
310
    }
311 1
312
    /**
313
     * Returns the path of static files directory, with a target.
314
     */
315 1
    public function getStaticTargetPath(): string
316
    {
317
        $path = $this->getStaticPath();
318
319
        if (!empty($this->get('static.target'))) {
320
            $path = substr($path, 0, -strlen((string) $this->get('static.target')));
321 1
        }
322
323 1
        return $path;
324
    }
325
326
    /**
327
     * Returns the path of assets files directory.
328
     */
329
    public function getAssetsPath(): string
330 1
    {
331
        return Util::joinFile($this->getSourceDir(), (string) $this->get('assets.dir'));
332 1
    }
333 1
334
    /**
335
     * Is cache dir is absolute to system files
336
     * or relative to project destination?
337 1
     */
338
    public function isCacheDirIsAbsolute(): bool
339
    {
340
        $path = (string) $this->get('cache.dir');
341
        if (Util::joinFile($path) == realpath(Util::joinFile($path))) {
342
            return true;
343
        }
344
345 1
        return false;
346
    }
347 1
348
    /**
349
     * Returns cache path.
350
     *
351 1
     * @throws RuntimeException
352
     */
353
    public function getCachePath(): string
354
    {
355
        if (empty((string) $this->get('cache.dir'))) {
356
            throw new RuntimeException(\sprintf('The cache directory ("%s") is not defined in configuration.', 'cache.dir'));
357
        }
358 1
359
        if ($this->isCacheDirIsAbsolute()) {
360
            $cacheDir = Util::joinFile((string) $this->get('cache.dir'), 'cecil');
361
            Util\File::getFS()->mkdir($cacheDir);
362
363
            return $cacheDir;
364 1
        }
365
366 1
        return Util::joinFile($this->getDestinationDir(), (string) $this->get('cache.dir'));
367
    }
368
369
    /**
370
     * Returns cache path of remote assets.
371
     */
372
    public function getCacheAssetsPath(): string
373
    {
374
        return Util::joinFile($this->getCachePath(), (string) $this->get('cache.assets.dir'));
375
    }
376 1
377
    /**
378 1
     * Returns the property value of an output format.
379
     *
380 1
     * @throws RuntimeException
381
     *
382
     * @return string|array|null
383
     */
384 1
    public function getOutputFormatProperty(string $name, string $property)
385
    {
386
        $properties = array_column((array) $this->get('output.formats'), $property, 'name');
387
388
        if (empty($properties)) {
389
            throw new RuntimeException(\sprintf('Property "%s" is not defined for format "%s".', $property, $name));
390
        }
391
392
        return $properties[$name] ?? null;
393
    }
394 1
395
    /**
396 1
     * Theme helpers.
397 1
     */
398 1
399
    /**
400
     * Returns theme(s) as an array.
401
     */
402
    public function getTheme(): ?array
403
    {
404
        if ($themes = $this->get('theme')) {
405
            if (is_array($themes)) {
406
                return $themes;
407
            }
408
409
            return [$themes];
410
        }
411
412 1
        return null;
413
    }
414 1
415 1
    /**
416 1
     * Has a (valid) theme(s)?
417
     *
418
     * @throws RuntimeException
419
     */
420
    public function hasTheme(): bool
421 1
    {
422
        if ($themes = $this->getTheme()) {
423
            foreach ($themes as $theme) {
424
                if (!Util\File::getFS()->exists($this->getThemeDirPath($theme, 'layouts'))) {
425
                    throw new RuntimeException(\sprintf('Theme directory "%s" not found!', Util::joinFile($this->getThemesPath(), $theme, 'layouts')));
426
                }
427
            }
428
429
            return true;
430
        }
431 1
432
        return false;
433 1
    }
434
435
    /**
436
     * Returns the path of a specific theme's directory.
437
     * ("layouts" by default).
438
     */
439
    public function getThemeDirPath(string $theme, string $dir = 'layouts'): string
440
    {
441
        return Util::joinFile($this->getThemesPath(), $theme, $dir);
442
    }
443
444
    /**
445 1
     * Language helpers.
446
     */
447 1
448 1
    /**
449
     * Returns an array of available languages.
450
     *
451 1
     * @throws RuntimeException
452
     */
453 1
    public function getLanguages(): array
454
    {
455
        if ($this->languages !== null) {
456
            return $this->languages;
457 1
        }
458 1
459
        $languages = (array) $this->get('languages');
460
461 1
        if (!is_int(array_search($this->getLanguageDefault(), array_column($languages, 'code')))) {
462
            throw new RuntimeException(\sprintf('The default language "%s" is not listed in "languages" key configuration.', $this->getLanguageDefault()));
463 1
        }
464
465
        $languages = array_filter($languages, function ($language) {
466
            return !(isset($language['enabled']) && $language['enabled'] === false);
467
        });
468
469
        $this->languages = $languages;
470
471 1
        return $this->languages;
472
    }
473 1
474
    /**
475
     * Returns the default language code (ie: "en", "fr-fr", etc.).
476
     *
477 1
     * @throws RuntimeException
478
     */
479
    public function getLanguageDefault(): string
480
    {
481
        if (!$this->get('language')) {
482
            throw new RuntimeException('There is no default "language" key in configuration.');
483
        }
484
485 1
        return $this->get('language');
486
    }
487 1
488
    /**
489 1
     * Returns a language code index.
490
     *
491
     * @throws RuntimeException
492
     */
493 1
    public function getLanguageIndex(string $code): int
494
    {
495
        $array = array_column($this->getLanguages(), 'code');
496
497
        if (false === $index = array_search($code, $array)) {
498
            throw new RuntimeException(\sprintf('The language code "%s" is not defined.', $code));
499
        }
500
501 1
        return $index;
502
    }
503 1
504
    /**
505 1
     * Returns the property value of a (specified or default) language.
506
     *
507 1
     * @throws RuntimeException
508
     */
509
    public function getLanguageProperty(string $property, string $code = null): string
510
    {
511 1
        $code = $code ?? $this->getLanguageDefault();
512
513
        $properties = array_column($this->getLanguages(), $property, 'code');
514
515
        if (empty($properties)) {
516
            throw new RuntimeException(\sprintf('Property "%s" is not defined for language "%s".', $property, $code));
517
        }
518
519
        return $properties[$code];
520
    }
521
}
522