Passed
Push — fix/config ( 8b6a9e )
by Arnaud
05:38
created

Config::setSourceDir()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 11
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
    /** @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
     * Import 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)), $getEnv);
75
                }
76
                $iterator->next();
77
            }
78
        };
79
        $applyEnv($data->export());
80
    }
81
82
    /**
83
     * Import (theme) configuration.
84
     *
85
     * @param array|null $config
86
     *
87
     * @return void
88
     */
89
    public function import(array $config): void
90
    {
91
        $this->data->import($config);
92
93
        // re-import site config
94
        $this->importSiteConfig();
95
    }
96
97
    /**
98
     * Set a Data object as configuration.
99
     *
100
     * @param Data $data
101
     *
102
     * @return self
103
     */
104
    protected function setData(Data $data): self
105
    {
106
        if ($this->data !== $data) {
107
            $this->data = $data;
108
        }
109
110
        return $this;
111
    }
112
113
    /**
114
     * Get configuration as a Data object.
115
     *
116
     * @return Data
117
     */
118
    public function getData(): Data
119
    {
120
        return $this->data;
121
    }
122
123
    /**
124
     * Get configuration as an array.
125
     *
126
     * @return array
127
     */
128
    public function getAsArray(): array
129
    {
130
        return $this->data->export();
131
    }
132
133
    /**
134
     * Is configuration's key' exists?
135
     *
136
     * @param string $key
137
     *
138
     * @return bool
139
     */
140
    public function has(string $key): bool
141
    {
142
        return $this->data->has($key);
143
    }
144
145
    /**
146
     * Get the value of a configuration's key'.
147
     *
148
     * @param string      $key
149
     * @param string|null $language
150
     *
151
     * @return mixed|null
152
     */
153
    public function get(string $key, string $language = null)
154
    {
155
        if ($language !== null) {
156
            $index = $this->getLanguageIndex($language);
157
            $keyLang = sprintf('languages.%s.config.%s', $index, $key);
158
            if ($this->data->has($keyLang)) {
159
                return $this->data->get($keyLang);
160
            }
161
        }
162
163
        return $this->data->get($key);
164
    }
165
166
    /**
167
     * Set the source directory.
168
     *
169
     * @param string|null $sourceDir
170
     *
171
     * @throws \InvalidArgumentException
172
     *
173
     * @return self
174
     */
175
    public function setSourceDir(string $sourceDir = null): self
176
    {
177
        if ($sourceDir === null) {
178
            $sourceDir = getcwd();
179
        }
180
        if (!is_dir($sourceDir)) {
181
            throw new \InvalidArgumentException(sprintf('The directory "%s" is not a valid source!', $sourceDir));
182
        }
183
        $this->sourceDir = $sourceDir;
184
185
        return $this;
186
    }
187
188
    /**
189
     * Get the source directory.
190
     *
191
     * @return string
192
     */
193
    public function getSourceDir(): string
194
    {
195
        return $this->sourceDir;
196
    }
197
198
    /**
199
     * Set the destination directory.
200
     *
201
     * @param string|null $destinationDir
202
     *
203
     * @throws \InvalidArgumentException
204
     *
205
     * @return self
206
     */
207
    public function setDestinationDir(string $destinationDir = null): self
208
    {
209
        if ($destinationDir === null) {
210
            $destinationDir = $this->sourceDir;
211
        }
212
        if (!is_dir($destinationDir)) {
213
            throw new \InvalidArgumentException(sprintf(
214
                'The directory "%s" is not a valid destination!',
215
                $destinationDir
216
            ));
217
        }
218
        $this->destinationDir = $destinationDir;
219
220
        return $this;
221
    }
222
223
    /**
224
     * Get the destination directory.
225
     *
226
     * @return string
227
     */
228
    public function getDestinationDir(): string
229
    {
230
        return $this->destinationDir;
231
    }
232
233
    /**
234
     * Path helpers.
235
     */
236
237
    /**
238
     * Return the path of the content directory.
239
     *
240
     * @return string
241
     */
242
    public function getContentPath(): string
243
    {
244
        return Util::joinFile($this->getSourceDir(), (string) $this->get('content.dir'));
245
    }
246
247
    /**
248
     * Return the path of the data directory.
249
     *
250
     * @return string
251
     */
252
    public function getDataPath(): string
253
    {
254
        return Util::joinFile($this->getSourceDir(), (string) $this->get('data.dir'));
255
    }
256
257
    /**
258
     * Return the path of templates directory.
259
     *
260
     * @return string
261
     */
262
    public function getLayoutsPath(): string
263
    {
264
        return Util::joinFile($this->getSourceDir(), (string) $this->get('layouts.dir'));
265
    }
266
267
    /**
268
     * Return the path of themes directory.
269
     *
270
     * @return string
271
     */
272
    public function getThemesPath(): string
273
    {
274
        return Util::joinFile($this->getSourceDir(), (string) $this->get('themes.dir'));
275
    }
276
277
    /**
278
     * Return the path of internal templates directory.
279
     *
280
     * @return string
281
     */
282
    public function getInternalLayoutsPath(): string
283
    {
284
        return Util::joinPath(__DIR__, '..', (string) $this->get('layouts.internal.dir'));
285
    }
286
287
    /**
288
     * Return the path of the output directory.
289
     *
290
     * @return string
291
     */
292
    public function getOutputPath(): string
293
    {
294
        return Util::joinFile($this->getDestinationDir(), (string) $this->get('output.dir'));
295
    }
296
297
    /**
298
     * Return the path of static files directory.
299
     *
300
     * @return string
301
     */
302
    public function getStaticPath(): string
303
    {
304
        return Util::joinFile($this->getSourceDir(), (string) $this->get('static.dir'));
305
    }
306
307
    /**
308
     * Is cache dir is absolute to system files
309
     * or relative to project destination?
310
     *
311
     * @return bool
312
     */
313
    public function isCacheDirIsAbsolute(): bool
314
    {
315
        $path = (string) $this->get('cache.dir');
316
        if (Util::joinFile($path) == realpath(Util::joinFile($path))) {
317
            return true;
318
        }
319
320
        return false;
321
    }
322
323
    /**
324
     * Return cache path.
325
     *
326
     * @return string
327
     */
328
    public function getCachePath(): string
329
    {
330
        if (empty((string) $this->get('cache.dir'))) {
331
            throw new Exception(\sprintf('The cache directory ("%s") is not defined in configuration.', 'cache.dir'));
332
        }
333
334
        if ($this->isCacheDirIsAbsolute()) {
335
            $cacheDir = Util::joinFile((string) $this->get('cache.dir'));
336
            $cacheDir = Util::joinFile($cacheDir, 'cecil');
337
            Util::getFS()->mkdir($cacheDir);
338
339
            return $cacheDir;
340
        }
341
342
        return Util::joinFile($this->getDestinationDir(), (string) $this->get('cache.dir'));
343
    }
344
345
    /**
346
     * Return the property value of an output format.
347
     *
348
     * @param string $name
349
     * @param string $property
350
     *
351
     * @return string|array|null
352
     */
353
    public function getOutputFormatProperty(string $name, string $property)
354
    {
355
        $properties = array_column((array) $this->get('output.formats'), $property, 'name');
356
357
        if (empty($properties)) {
358
            throw new Exception(sprintf(
359
                'Property "%s" is not defined for format "%s".',
360
                $property,
361
                $name
362
            ));
363
        }
364
365
        if (!array_key_exists($name, $properties)) {
366
            return null;
367
        }
368
369
        return $properties[$name];
370
    }
371
372
    /**
373
     * Theme helpers.
374
     */
375
376
    /**
377
     * Return theme(s) as an array.
378
     *
379
     * @return array|null
380
     */
381
    public function getTheme(): ?array
382
    {
383
        if ($themes = $this->get('theme')) {
384
            if (is_array($themes)) {
385
                return $themes;
386
            }
387
388
            return [$themes];
389
        }
390
391
        return null;
392
    }
393
394
    /**
395
     * Has a (valid) theme(s)?
396
     *
397
     * @throws Exception
398
     *
399
     * @return bool
400
     */
401
    public function hasTheme(): bool
402
    {
403
        if ($themes = $this->getTheme()) {
404
            foreach ($themes as $theme) {
405
                if (!Util::getFS()->exists($this->getThemeDirPath($theme, 'layouts'))) {
406
                    throw new Exception(sprintf(
407
                        'Theme directory "%s" not found!',
408
                        Util::joinFile($this->getThemesPath(), $theme, 'layouts')
409
                    ));
410
                }
411
            }
412
413
            return true;
414
        }
415
416
        return false;
417
    }
418
419
    /**
420
     * Return the path of a specific theme's directory.
421
     * ("layouts" by default).
422
     *
423
     * @param string $theme
424
     * @param string $dir
425
     *
426
     * @return string
427
     */
428
    public function getThemeDirPath(string $theme, string $dir = 'layouts'): string
429
    {
430
        return Util::joinFile($this->getThemesPath(), $theme, $dir);
431
    }
432
433
    /**
434
     * Language helpers.
435
     */
436
437
    /**
438
     * Return an array of available languages.
439
     *
440
     * @return array
441
     */
442
    public function getLanguages(): array
443
    {
444
        return $this->get('languages');
445
    }
446
447
    /**
448
     * Return the default language code (ie: "en", "fr-fr", etc.).
449
     *
450
     * @return string
451
     */
452
    public function getLanguageDefault(): string
453
    {
454
        if (!$this->get('language')) {
455
            throw new Exception('There is no default "language" in configuration.');
456
        }
457
458
        return $this->get('language');
459
    }
460
461
    /**
462
     * Return a language code index.
463
     *
464
     * @param string $code
465
     *
466
     * @return int
467
     */
468
    public function getLanguageIndex(string $code): int
469
    {
470
        $array = array_column($this->getLanguages(), 'code');
471
472
        if (!$index = array_search($code, $array)) {
473
            throw new Exception(sprintf('The language code "%s" is not defined.', $code));
474
        }
475
476
        return $index;
477
    }
478
479
    /**
480
     * Return the property value of a (specified or default) language.
481
     *
482
     * @param string      $property
483
     * @param string|null $code
484
     *
485
     * @return string|null
486
     */
487
    public function getLanguageProperty(string $property, string $code = null): ?string
488
    {
489
        $code = $code ?? $this->getLanguageDefault();
490
491
        $properties = array_column($this->getLanguages(), $property, 'code');
492
493
        if (empty($properties)) {
494
            throw new Exception(sprintf(
495
                'Property "%s" is not defined for language "%s".',
496
                $property,
497
                $code
498
            ));
499
        }
500
501
        return $properties[$code];
502
    }
503
}
504