Passed
Pull Request — assets (#723)
by Arnaud
08:32 queued 05:48
created

Config   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 502
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 123
c 6
b 0
f 0
dl 0
loc 502
rs 5.5199
wmc 56

30 Methods

Rating   Name   Duplication   Size   Complexity  
A setData() 0 7 2
A has() 0 3 1
A get() 0 11 3
A getAsArray() 0 3 1
A getData() 0 3 1
A importSiteConfig() 0 27 4
A __construct() 0 12 2
A import() 0 6 1
A setDestinationDir() 0 14 3
A getThemeDirPath() 0 3 1
A getLanguageIndex() 0 9 2
A getCachePath() 0 11 2
A getInternalLayoutsPath() 0 3 1
A getOutputFormatProperty() 0 17 3
A getContentPath() 0 3 1
A getThemesPath() 0 3 1
A getDestinationDir() 0 3 1
A getLanguageDefault() 0 7 2
A getStaticPath() 0 3 1
A castGetValue() 0 14 4
A setSourceDir() 0 11 3
A hasTheme() 0 17 4
A getLanguageProperty() 0 15 2
A getSourceDir() 0 3 1
A getDataPath() 0 3 1
A isCacheDirIsAbsolute() 0 8 2
A getTheme() 0 11 3
A getLayoutsPath() 0 3 1
A getLanguages() 0 3 1
A getOutputPath() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Config often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Config, and based on these observations, apply Extract Interface, too.

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->castGetValue($this->data->get($keyLang));
160
            }
161
        }
162
163
        return $this->castGetValue($this->data->get($key));
164
    }
165
166
    /**
167
     * Cast value returned by get().
168
     *
169
     * @param mixed
170
     *
171
     * @return mixed
172
     */
173
    private function castGetValue($value)
174
    {
175
        if (is_string($value)) {
176
            switch ($value) {
177
                case 'true':
178
                    return true;
179
                case 'false':
180
                    return false;
181
                default:
182
                    return $value;
183
            }
184
        }
185
186
        return $value;
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
     * Return 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
     * Return 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
     * Return 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
     * Return 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
     * Return 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
     * Return 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
     * Return 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
     * Is cache dir is absolute to system files
332
     * or relative to project destination?
333
     *
334
     * @return bool
335
     */
336
    public function isCacheDirIsAbsolute(): bool
337
    {
338
        $path = (string) $this->get('cache.dir');
339
        if (Util::joinFile($path) == realpath(Util::joinFile($path))) {
340
            return true;
341
        }
342
343
        return false;
344
    }
345
346
    /**
347
     * Return cache path.
348
     *
349
     * @return string
350
     */
351
    public function getCachePath(): string
352
    {
353
        if ($this->isCacheDirIsAbsolute()) {
354
            $cacheDir = Util::joinFile((string) $this->get('cache.dir'));
355
            $cacheDir = Util::joinFile($cacheDir, 'cecil');
356
            Util::getFS()->mkdir($cacheDir);
357
358
            return $cacheDir;
359
        }
360
361
        return Util::joinFile($this->getDestinationDir(), (string) $this->get('cache.dir'));
362
    }
363
364
    /**
365
     * Return the property value of an output format.
366
     *
367
     * @param string $name
368
     * @param string $property
369
     *
370
     * @return string|array|null
371
     */
372
    public function getOutputFormatProperty(string $name, string $property)
373
    {
374
        $properties = array_column((array) $this->get('output.formats'), $property, 'name');
375
376
        if (empty($properties)) {
377
            throw new Exception(sprintf(
378
                'Property "%s" is not defined for format "%s".',
379
                $property,
380
                $name
381
            ));
382
        }
383
384
        if (!array_key_exists($name, $properties)) {
385
            return null;
386
        }
387
388
        return $properties[$name];
389
    }
390
391
    /**
392
     * Theme helpers.
393
     */
394
395
    /**
396
     * Return theme(s) as an array.
397
     *
398
     * @return array|null
399
     */
400
    public function getTheme(): ?array
401
    {
402
        if ($themes = $this->get('theme')) {
403
            if (is_array($themes)) {
404
                return $themes;
405
            }
406
407
            return [$themes];
408
        }
409
410
        return null;
411
    }
412
413
    /**
414
     * Has a (valid) theme(s)?
415
     *
416
     * @throws Exception
417
     *
418
     * @return bool
419
     */
420
    public function hasTheme(): bool
421
    {
422
        if ($themes = $this->getTheme()) {
423
            foreach ($themes as $theme) {
424
                if (!Util::getFS()->exists($this->getThemeDirPath($theme, 'layouts'))) {
425
                    throw new Exception(sprintf(
426
                        'Theme directory "%s/%s/layouts" not found!',
427
                        $this->getThemesPath(),
428
                        $theme
429
                    ));
430
                }
431
            }
432
433
            return true;
434
        }
435
436
        return false;
437
    }
438
439
    /**
440
     * Return the path of a specific theme's directory.
441
     * ("layouts" by default).
442
     *
443
     * @param string $theme
444
     * @param string $dir
445
     *
446
     * @return string
447
     */
448
    public function getThemeDirPath(string $theme, string $dir = 'layouts'): string
449
    {
450
        return Util::joinFile($this->getThemesPath(), $theme, $dir);
451
    }
452
453
    /**
454
     * Language helpers.
455
     */
456
457
    /**
458
     * Return an array of available languages.
459
     *
460
     * @return array
461
     */
462
    public function getLanguages(): array
463
    {
464
        return $this->get('languages');
465
    }
466
467
    /**
468
     * Return the default language code (ie: "en", "fr-fr", etc.).
469
     *
470
     * @return string
471
     */
472
    public function getLanguageDefault(): string
473
    {
474
        if (!$this->get('language')) {
475
            throw new Exception('There is no default "language" in configuration.');
476
        }
477
478
        return $this->get('language');
479
    }
480
481
    /**
482
     * Return a language code index.
483
     *
484
     * @param string $code
485
     *
486
     * @return int
487
     */
488
    public function getLanguageIndex(string $code): int
489
    {
490
        $array = array_column($this->getLanguages(), 'code');
491
492
        if (!$index = array_search($code, $array)) {
493
            throw new Exception(sprintf('The language code "%s" is not defined.', $code));
494
        }
495
496
        return $index;
497
    }
498
499
    /**
500
     * Return the property value of a (specified or default) language.
501
     *
502
     * @param string      $property
503
     * @param string|null $code
504
     *
505
     * @return string|null
506
     */
507
    public function getLanguageProperty(string $property, string $code = null): ?string
508
    {
509
        $code = $code ?? $this->getLanguageDefault();
510
511
        $properties = array_column($this->getLanguages(), $property, 'code');
512
513
        if (empty($properties)) {
514
            throw new Exception(sprintf(
515
                'Property "%s" is not defined for language "%s".',
516
                $property,
517
                $code
518
            ));
519
        }
520
521
        return $properties[$code];
522
    }
523
}
524