Passed
Pull Request — master (#1877)
by Arnaud
12:13 queued 05:29
created

Builder::__construct()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 2
dl 0
loc 15
ccs 7
cts 8
cp 0.875
crap 4.0312
rs 10
c 0
b 0
f 0
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
    public const VERSION = '8.x-dev';
31
    public const VERBOSITY_QUIET = -1;
32
    public const VERBOSITY_NORMAL = 0;
33
    public const VERBOSITY_VERBOSE = 1;
34
    public 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\Optimize\Html',
53
        'Cecil\Step\Optimize\Css',
54
        'Cecil\Step\Optimize\Js',
55
        'Cecil\Step\Optimize\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 array 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
    /**
98
     * @param Config|array|null    $config
99
     * @param LoggerInterface|null $logger
100
     */
101 1
    public function __construct($config = null, LoggerInterface $logger = null)
102
    {
103
        // set logger
104 1
        if ($logger === null) {
105
            $logger = new PrintLogger(self::VERBOSITY_VERBOSE);
106
        }
107 1
        $this->setLogger($logger);
108
        // 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 1
            $this->debug = true;
113
        }
114
        // autoloads local extensions
115 1
        Util::autoload($this, 'extensions');
116
    }
117
118
    /**
119
     * Creates a new Builder instance.
120
     */
121 1
    public static function create(): self
122
    {
123 1
        $class = new \ReflectionClass(\get_called_class());
124
125 1
        return $class->newInstanceArgs(\func_get_args());
126
    }
127
128
    /**
129
     * Builds a new website.
130
     */
131 1
    public function build(array $options): self
132
    {
133
        // set start script time and memory usage
134 1
        $startTime = microtime(true);
135 1
        $startMemory = memory_get_usage();
136
137
        // checks the configuration
138 1
        $this->validConfig();
139
140
        // prepare options
141 1
        $this->options = array_merge([
142 1
            'drafts'  => false, // build drafts or not
143 1
            'dry-run' => false, // if dry-run is true, generated files are not saved
144 1
            'page'    => '',    // specific page to build
145 1
        ], $options);
146
147
        // process each step
148 1
        $steps = [];
149
        // init...
150 1
        foreach ($this->steps as $step) {
151
            /** @var Step\StepInterface $stepObject */
152 1
            $stepObject = new $step($this);
153 1
            $stepObject->init($this->options);
154 1
            if ($stepObject->canProcess()) {
155 1
                $steps[] = $stepObject;
156
            }
157
        }
158
        // ...and process!
159 1
        $stepNumber = 0;
160 1
        $stepsTotal = \count($steps);
161 1
        foreach ($steps as $step) {
162 1
            $stepNumber++;
163
            /** @var Step\StepInterface $step */
164 1
            $this->getLogger()->notice($step->getName(), ['step' => [$stepNumber, $stepsTotal]]);
165 1
            $stepStartTime = microtime(true);
166 1
            $stepStartMemory = memory_get_usage();
167 1
            $step->process();
168 1
            $this->getLogger()->info(sprintf('%s done in %s (%s)', $step->getName(), Util::convertMicrotime((float) $stepStartTime), Util::convertMemory(memory_get_usage() - $stepStartMemory)));
169
        }
170
171
        // process duration
172 1
        $this->getLogger()->notice(sprintf('Built in %s (%s)', Util::convertMicrotime($startTime), Util::convertMemory(memory_get_usage() - $startMemory)));
0 ignored issues
show
Bug introduced by
It seems like $startTime can also be of type string; however, parameter $start of Cecil\Util::convertMicrotime() does only seem to accept double, 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

172
        $this->getLogger()->notice(sprintf('Built in %s (%s)', Util::convertMicrotime(/** @scrutinizer ignore-type */ $startTime), Util::convertMemory(memory_get_usage() - $startMemory)));
Loading history...
173
174 1
        return $this;
175
    }
176
177
    /**
178
     * Set configuration.
179
     *
180
     * @param Config|array|null $config
181
     */
182 1
    public function setConfig($config): self
183
    {
184 1
        if (!$config instanceof Config) {
185 1
            $config = new Config($config);
186
        }
187 1
        if ($this->config !== $config) {
188 1
            $this->config = $config;
189
        }
190
191 1
        return $this;
192
    }
193
194
    /**
195
     * Returns configuration.
196
     */
197 1
    public function getConfig(): Config
198
    {
199 1
        return $this->config;
200
    }
201
202
    /**
203
     * Config::setSourceDir() alias.
204
     */
205 1
    public function setSourceDir(string $sourceDir = null): self
206
    {
207 1
        $this->config->setSourceDir($sourceDir);
208
209 1
        return $this;
210
    }
211
212
    /**
213
     * Config::setDestinationDir() alias.
214
     */
215 1
    public function setDestinationDir(string $destinationDir = null): self
216
    {
217 1
        $this->config->setDestinationDir($destinationDir);
218
219 1
        return $this;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 1
    public function setLogger(LoggerInterface $logger): void
226
    {
227 1
        $this->logger = $logger;
228
    }
229
230
    /**
231
     * Returns the logger instance.
232
     */
233 1
    public function getLogger(): LoggerInterface
234
    {
235 1
        return $this->logger;
236
    }
237
238
    /**
239
     * Returns debug mode state.
240
     */
241 1
    public function isDebug(): bool
242
    {
243 1
        return $this->debug;
244
    }
245
246
    /**
247
     * Returns build options.
248
     */
249 1
    public function getBuildOptions(): array
250
    {
251 1
        return $this->options;
252
    }
253
254
    /**
255
     * Set collected pages files.
256
     */
257 1
    public function setPagesFiles(Finder $content): void
258
    {
259 1
        $this->content = $content;
260
    }
261
262
    /**
263
     * Returns pages files.
264
     */
265 1
    public function getPagesFiles(): ?Finder
266
    {
267 1
        return $this->content;
268
    }
269
270
    /**
271
     * Set collected data.
272
     */
273 1
    public function setData(array $data): void
274
    {
275 1
        $this->data = $data;
276
    }
277
278
    /**
279
     * Returns data collection.
280
     */
281 1
    public function getData(): array
282
    {
283 1
        return $this->data;
284
    }
285
286
    /**
287
     * Set collected static files.
288
     */
289 1
    public function setStatic(array $static): void
290
    {
291 1
        $this->static = $static;
292
    }
293
294
    /**
295
     * Returns static files collection.
296
     */
297 1
    public function getStatic(): array
298
    {
299 1
        return $this->static;
300
    }
301
302
    /**
303
     * Set/update Pages collection.
304
     */
305 1
    public function setPages(PagesCollection $pages): void
306
    {
307 1
        $this->pages = $pages;
308
    }
309
310
    /**
311
     * Returns pages collection.
312
     */
313 1
    public function getPages(): ?PagesCollection
314
    {
315 1
        return $this->pages;
316
    }
317
318
    /**
319
     * Set menus collection.
320
     */
321 1
    public function setMenus(array $menus): void
322
    {
323 1
        $this->menus = $menus;
324
    }
325
326
    /**
327
     * Returns all menus, for a language.
328
     */
329 1
    public function getMenus(string $language): Collection\Menu\Collection
330
    {
331 1
        return $this->menus[$language];
332
    }
333
334
    /**
335
     * Set taxonomies collection.
336
     */
337 1
    public function setTaxonomies(array $taxonomies): void
338
    {
339 1
        $this->taxonomies = $taxonomies;
340
    }
341
342
    /**
343
     * Returns taxonomies collection, for a language.
344
     */
345 1
    public function getTaxonomies(string $language): ?Collection\Taxonomy\Collection
346
    {
347 1
        return $this->taxonomies[$language];
348
    }
349
350
    /**
351
     * Set renderer object.
352
     */
353 1
    public function setRenderer(Renderer\RendererInterface $renderer): void
354
    {
355 1
        $this->renderer = $renderer;
356
    }
357
358
    /**
359
     * Returns Renderer object.
360
     */
361 1
    public function getRenderer(): Renderer\RendererInterface
362
    {
363 1
        return $this->renderer;
364
    }
365
366
    /**
367
     * Returns application version.
368
     *
369
     * @throws RuntimeException
370
     */
371 1
    public static function getVersion(): string
372
    {
373 1
        if (!isset(self::$version)) {
374 1
            $filePath = __DIR__ . '/../VERSION';
375 1
            if (Plateform::isPhar()) {
376
                $filePath = Plateform::getPharPath() . '/VERSION';
377
            }
378
379
            try {
380 1
                if (!file_exists($filePath)) {
381 1
                    throw new RuntimeException(sprintf('File "%s" doesn\'t exist.', $filePath));
382
                }
383
                $version = Util\File::fileGetContents($filePath);
384
                if ($version === false) {
385
                    throw new RuntimeException(sprintf('Can\'t get file "%s".', $filePath));
386
                }
387
                self::$version = trim($version);
388 1
            } catch (\Exception) {
389 1
                self::$version = self::VERSION;
390
            }
391
        }
392
393 1
        return self::$version;
394
    }
395
396
    /**
397
     * Checks the configuration.
398
     */
399 1
    protected function validConfig(): void
400
    {
401
        // baseurl
402 1
        if (empty(trim((string) $this->config->get('baseurl'), '/'))) {
403
            $this->getLogger()->error('Config: `baseurl` is required in production (e.g.: "baseurl: https://example.com/").');
404
        }
405
        // default language
406 1
        if (!preg_match('/^' . Config::LANG_CODE_PATTERN . '$/', $this->config->getLanguageDefault())) {
407
            throw new RuntimeException(sprintf('Config: default language code "%s" is not valid (e.g.: "language: fr-FR").', $this->config->getLanguageDefault()));
408
        }
409
        // locales
410 1
        foreach ($this->config->getLanguages() as $lang) {
411 1
            if (!isset($lang['locale'])) {
412
                throw new RuntimeException('Config: a language locale is not defined.');
413
            }
414 1
            if (!preg_match('/^' . Config::LANG_LOCALE_PATTERN . '$/', $lang['locale'])) {
415
                throw new RuntimeException(sprintf('Config: the language locale "%s" is not valid (e.g.: "locale: fr_FR").', $lang['locale']));
416
            }
417
        }
418
    }
419
}
420