Passed
Push — config ( c05570 )
by Arnaud
04:09
created

Builder   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Test Coverage

Coverage 94.17%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 115
dl 0
loc 380
ccs 97
cts 103
cp 0.9417
rs 8.8798
c 1
b 1
f 0
wmc 44

27 Methods

Rating   Name   Duplication   Size   Complexity  
A getLogger() 0 3 1
A getConfig() 0 3 1
A setMenus() 0 3 1
A getRenderer() 0 3 1
A getData() 0 3 1
A setPages() 0 3 1
A setTaxonomies() 0 3 1
A setRenderer() 0 3 1
A getPages() 0 3 1
A getMenus() 0 3 1
A getStatic() 0 3 1
A getBuildOptions() 0 3 1
A setStatic() 0 3 1
A setLogger() 0 3 1
A isDebug() 0 3 1
A setData() 0 3 1
A getTaxonomies() 0 3 1
A __construct() 0 12 4
A create() 0 5 1
A setDestinationDir() 0 5 1
A getVersion() 0 23 6
A setPagesFiles() 0 3 1
A build() 0 41 4
A setSourceDir() 0 5 1
A validConfig() 0 14 5
A setConfig() 0 10 3
A getPagesFiles() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Builder 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 Builder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil;
15
16
use Cecil\Collection\Page\Collection as PagesCollection;
17
use Cecil\Exception\RuntimeException;
18
use Cecil\Generator\GeneratorManager;
19
use Cecil\Logger\PrintLogger;
20
use Cecil\Util\Plateform;
21
use Psr\Log\LoggerAwareInterface;
22
use Psr\Log\LoggerInterface;
23
use Symfony\Component\Finder\Finder;
24
25
/**
26
 * Class Builder.
27
 */
28
class Builder implements LoggerAwareInterface
29
{
30
    const VERSION = '7.x-dev';
31
    const VERBOSITY_QUIET = -1;
32
    const VERBOSITY_NORMAL = 0;
33
    const VERBOSITY_VERBOSE = 1;
34
    const VERBOSITY_DEBUG = 2;
35
36
    /**
37
     * @var array Steps processed by build().
38
     */
39
    protected $steps = [
40
        'Cecil\Step\Themes\Import',
41
        'Cecil\Step\Pages\Load',
42
        'Cecil\Step\Data\Load',
43
        'Cecil\Step\StaticFiles\Load',
44
        'Cecil\Step\Pages\Create',
45
        'Cecil\Step\Pages\Convert',
46
        'Cecil\Step\Taxonomies\Create',
47
        'Cecil\Step\Pages\Generate',
48
        'Cecil\Step\Menus\Create',
49
        'Cecil\Step\StaticFiles\Copy',
50
        'Cecil\Step\Pages\Render',
51
        'Cecil\Step\Pages\Save',
52
        'Cecil\Step\PostProcess\Html',
53
        'Cecil\Step\PostProcess\Css',
54
        'Cecil\Step\PostProcess\Js',
55
        'Cecil\Step\PostProcess\Images',
56
    ];
57
58
    /** @var Config Configuration. */
59
    protected $config;
60
61
    /** @var LoggerInterface Logger. */
62
    protected $logger;
63
64
    /** @var bool Debug mode. */
65
    protected $debug = false;
66
67
    /** @var array Build options. */
68
    protected $options;
69
70
    /** @var Finder Content iterator. */
71
    protected $content;
72
73
    /** @var array Data collection. */
74
    protected $data = [];
75
76
    /** @var array Static files collection. */
77
    protected $static = [];
78
79
    /** @var PagesCollection Pages collection. */
80
    protected $pages;
81
82
    /** @var array Menus collection. */
83
    protected $menus;
84
85
    /** @var Collection\Taxonomy\Collection Taxonomies collection. */
86
    protected $taxonomies;
87
88
    /** @var Renderer\RendererInterface Renderer. */
89
    protected $renderer;
90
91
    /** @var GeneratorManager Generators manager. */
92
    protected $generatorManager;
93
94
    /** @var string Application version. */
95
    protected static $version;
96
97 1
    /**
98
     * @param Config|array|null    $config
99 1
     * @param LoggerInterface|null $logger
100
     */
101
    public function __construct($config = null, LoggerInterface $logger = null)
102 1
    {
103
        // set logger
104
        if ($logger === null) {
105 1
            $logger = new PrintLogger(self::VERBOSITY_VERBOSE);
106
        }
107
        $this->setLogger($logger);
108 1
        // set config
109 1
        $this->setConfig($config)->setSourceDir(null)->setDestinationDir(null);
110
        // debug mode?
111 1
        if (getenv('CECIL_DEBUG') == 'true' || (bool) $this->getConfig()->get('debug')) {
112
            $this->debug = true;
113
        }
114
    }
115
116 1
    /**
117
     * Creates a new Builder instance.
118 1
     */
119
    public static function create(): self
120 1
    {
121
        $class = new \ReflectionClass(get_called_class());
122
123
        return $class->newInstanceArgs(func_get_args());
124
    }
125
126 1
    /**
127
     * Builds a new website.
128
     */
129 1
    public function build(array $options): self
130
    {
131 1
        // set start script time
132 1
        $startTime = microtime(true);
133
134 1
        // checks the configuration
135
        $this->validConfig();
136
137 1
        // prepare options
138
        $this->options = array_merge([
139 1
            'drafts'  => false, // build drafts or not
140
            'dry-run' => false, // if dry-run is true, generated files are not saved
141 1
            'page'    => '',    // specific page to build
142 1
        ], $options);
143 1
144 1
        // process each step
145
        $steps = [];
146
        // init...
147
        foreach ($this->steps as $step) {
148 1
            /** @var Step\StepInterface $stepObject */
149 1
            $stepObject = new $step($this);
150 1
            $stepObject->init($this->options);
151 1
            if ($stepObject->canProcess()) {
152
                $steps[] = $stepObject;
153 1
            }
154 1
        }
155
        // ...and process!
156
        $stepNumber = 0;
157
        $stepsTotal = count($steps);
158 1
        foreach ($steps as $step) {
159 1
            $stepNumber++;
160
            /** @var Step\StepInterface $step */
161 1
            $this->getLogger()->notice($step->getName(), ['step' => [$stepNumber, $stepsTotal]]);
162
            $step->process();
163
        }
164
165
        // process duration
166
        $message = \sprintf('Built in %ss', round(microtime(true) - $startTime, 2));
167
        $this->getLogger()->notice($message);
168
169 1
        return $this;
170
    }
171 1
172 1
    /**
173
     * Set configuration.
174 1
     *
175 1
     * @param Config|array|null $config
176
     */
177
    public function setConfig($config): self
178 1
    {
179
        if (!$config instanceof Config) {
180
            $config = new Config($config);
181
        }
182
        if ($this->config !== $config) {
183
            $this->config = $config;
184 1
        }
185
186 1
        return $this;
187
    }
188
189
    /**
190
     * Returns configuration.
191
     */
192 1
    public function getConfig(): Config
193
    {
194 1
        return $this->config;
195
    }
196 1
197
    /**
198
     * Config::setSourceDir() alias.
199
     */
200
    public function setSourceDir(string $sourceDir = null): self
201
    {
202 1
        $this->config->setSourceDir($sourceDir);
203
204 1
        return $this;
205
    }
206 1
207
    /**
208
     * Config::setDestinationDir() alias.
209
     */
210
    public function setDestinationDir(string $destinationDir = null): self
211
    {
212 1
        $this->config->setDestinationDir($destinationDir);
213
214 1
        return $this;
215 1
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220 1
    public function setLogger(LoggerInterface $logger)
221
    {
222 1
        $this->logger = $logger;
223
    }
224
225
    /**
226
     * Returns the logger instance.
227
     */
228 1
    public function getLogger(): LoggerInterface
229
    {
230 1
        return $this->logger;
231
    }
232
233
    /**
234
     * Returns debug mode state.
235
     */
236 1
    public function isDebug(): bool
237
    {
238 1
        return $this->debug;
239
    }
240
241
    /**
242
     * Returns build options.
243
     */
244 1
    public function getBuildOptions(): array
245
    {
246 1
        return $this->options;
247 1
    }
248
249
    /**
250
     * Set collected pages files.
251
     */
252 1
    public function setPagesFiles(Finder $content): void
253
    {
254 1
        $this->content = $content;
255
    }
256
257
    /**
258
     * Returns pages files.
259
     */
260 1
    public function getPagesFiles(): ?Finder
261
    {
262 1
        return $this->content;
263 1
    }
264
265
    /**
266
     * Set collected data.
267
     */
268 1
    public function setData(array $data): void
269
    {
270 1
        $this->data = $data;
271
    }
272
273
    /**
274
     * Returns data collection.
275
     */
276 1
    public function getData(): array
277
    {
278 1
        return $this->data;
279 1
    }
280
281
    /**
282
     * Set collected static files.
283
     */
284 1
    public function setStatic(array $static): void
285
    {
286 1
        $this->static = $static;
287
    }
288
289
    /**
290
     * Returns static files collection.
291
     */
292 1
    public function getStatic(): array
293
    {
294 1
        return $this->static;
295 1
    }
296
297
    /**
298
     * Set/update Pages collection.
299
     */
300 1
    public function setPages(PagesCollection $pages): void
301
    {
302 1
        $this->pages = $pages;
303
    }
304
305
    /**
306
     * Returns pages collection.
307
     */
308 1
    public function getPages(): ?PagesCollection
309
    {
310 1
        return $this->pages;
311 1
    }
312
313
    /**
314
     * Set menus collection.
315
     */
316 1
    public function setMenus(array $menus): void
317
    {
318 1
        $this->menus = $menus;
319
    }
320
321
    /**
322
     * Returns all menus, for a language.
323
     */
324 1
    public function getMenus(string $language): Collection\Menu\Collection
325
    {
326 1
        return $this->menus[$language];
327 1
    }
328
329
    /**
330
     * Set taxonomies collection.
331
     */
332 1
    public function setTaxonomies(Collection\Taxonomy\Collection $taxonomies): void
333
    {
334 1
        $this->taxonomies = $taxonomies;
335
    }
336
337
    /**
338
     * Returns taxonomies collection.
339
     */
340 1
    public function getTaxonomies(): ?Collection\Taxonomy\Collection
341
    {
342 1
        return $this->taxonomies;
343 1
    }
344
345
    /**
346
     * Set renderer object.
347
     */
348 1
    public function setRenderer(Renderer\RendererInterface $renderer): void
349
    {
350 1
        $this->renderer = $renderer;
351
    }
352
353
    /**
354
     * Returns Renderer object.
355
     */
356 1
    public function getRenderer(): Renderer\RendererInterface
357
    {
358 1
        return $this->renderer;
359 1
    }
360 1
361
    /**
362
     * Returns application version.
363
     *
364
     * @throws RuntimeException
365 1
     */
366 1
    public static function getVersion(): string
367
    {
368
        if (!isset(self::$version)) {
369
            $filePath = __DIR__.'/../VERSION';
370
            if (Plateform::isPhar()) {
371
                $filePath = Plateform::getPharPath().'/VERSION';
372
            }
373 1
374 1
            try {
375
                if (!file_exists($filePath)) {
376
                    throw new RuntimeException(\sprintf('%s file doesn\'t exist!', $filePath));
377
                }
378 1
                $version = Util\File::fileGetContents($filePath);
379
                if ($version === false) {
380
                    throw new RuntimeException(\sprintf('Can\'t get %s file!', $filePath));
381
                }
382
                self::$version = trim($version);
383
            } catch (\Exception $e) {
384
                self::$version = self::VERSION;
385
            }
386
        }
387
388
        return self::$version;
389
    }
390
391
    /**
392
     * Checks the configuration.
393
     */
394
    protected function validConfig(): void
395
    {
396
        // baseurl
397
        if (empty(trim((string) $this->config->get('baseurl'), '/'))) {
398
            $this->getLogger()->error('The "baseurl" configuration key is required in production (e.g.: "baseurl: https://example.com/").');
399
        }
400
        // default language
401
        if (!preg_match('/^'.Config::LANG_CODE_PATTERN.'$/', $this->config->get('language'))) {
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('language') can also be of type null; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

401
        if (!preg_match('/^'.Config::LANG_CODE_PATTERN.'$/', /** @scrutinizer ignore-type */ $this->config->get('language'))) {
Loading history...
402
            throw new RuntimeException(\sprintf('The configured default language code "%s" is not valid (e.g.: "language: fr-FR").', $this->config->get('language')));
403
        }
404
        // locales
405
        foreach ($this->config->get('languages') as $lang) {
406
            if (!preg_match('/^'.Config::LANG_LOCALE_PATTERN.'$/', $lang['locale'])) {
407
                throw new RuntimeException(\sprintf('The configured language locale "%s" is not valid (e.g.: "locale: fr_FR").', $lang['locale']));
408
            }
409
        }
410
    }
411
}
412