Passed
Pull Request — master (#1013)
by lee
07:38
created

Config   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 525
Duplicated Lines 0 %

Test Coverage

Coverage 77.07%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 130
c 4
b 0
f 0
dl 0
loc 525
ccs 121
cts 157
cp 0.7707
rs 3.6
wmc 60

31 Methods

Rating   Name   Duplication   Size   Complexity  
A setData() 0 7 2
A has() 0 3 1
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 getThemeDirPath() 0 3 1
A getLanguageIndex() 0 9 2
A getCachePath() 0 15 3
A getInternalLayoutsPath() 0 3 1
A getContentPath() 0 3 1
A getThemesPath() 0 3 1
A getDestinationDir() 0 3 1
A getLanguageDefault() 0 7 2
A get() 0 15 4
A getStaticPath() 0 3 1
A setSourceDir() 0 11 3
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 getStaticTargetPath() 0 9 2
A getOutputPath() 0 3 1
A setDestinationDir() 0 14 3
A getOutputFormatProperty() 0 17 3
A castSetValue() 0 14 4
A hasTheme() 0 16 4
A getLanguageProperty() 0 15 2

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