Passed
Push — images ( 57e389...d8be70 )
by Arnaud
07:28 queued 11s
created

Config::getContentPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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