Passed
Push — assets-dir ( 82ec7e )
by Arnaud
05:22
created

Config::getAssetsPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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