Passed
Pull Request — master (#2148)
by Arnaud
09:31 queued 04:12
created

Builder::getBuilId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
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 Psr\Log\LoggerAwareInterface;
21
use Psr\Log\LoggerInterface;
22
use Symfony\Component\Finder\Finder;
23
24
/**
25
 * Class Builder.
26
 */
27
class Builder implements LoggerAwareInterface
28
{
29
    public const VERSION = '8.x-dev';
30
    public const VERBOSITY_QUIET = -1;
31
    public const VERBOSITY_NORMAL = 0;
32
    public const VERBOSITY_VERBOSE = 1;
33
    public const VERBOSITY_DEBUG = 2;
34
35
    /**
36
     * @var array Steps processed by build().
37
     */
38
    protected $steps = [
39
        'Cecil\Step\Pages\Load',
40
        'Cecil\Step\Data\Load',
41
        'Cecil\Step\StaticFiles\Load',
42
        'Cecil\Step\Pages\Create',
43
        'Cecil\Step\Pages\Convert',
44
        'Cecil\Step\Taxonomies\Create',
45
        'Cecil\Step\Pages\Generate',
46
        'Cecil\Step\Menus\Create',
47
        'Cecil\Step\StaticFiles\Copy',
48
        'Cecil\Step\Pages\Render',
49
        'Cecil\Step\Pages\Save',
50
        'Cecil\Step\Assets\Save',
51
        'Cecil\Step\Optimize\Html',
52
        'Cecil\Step\Optimize\Css',
53
        'Cecil\Step\Optimize\Js',
54
        'Cecil\Step\Optimize\Images',
55
    ];
56
57
    /** @var Config Configuration. */
58
    protected $config;
59
60
    /** @var LoggerInterface Logger. */
61
    protected $logger;
62
63
    /** @var bool Debug mode. */
64
    protected $debug = false;
65
66
    /** @var array Build options. */
67
    protected $options;
68
69
    /** @var Finder Content iterator. */
70
    protected $content;
71
72
    /** @var array Data collection. */
73
    protected $data = [];
74
75
    /** @var array Static files collection. */
76
    protected $static = [];
77
78
    /** @var PagesCollection Pages collection. */
79
    protected $pages;
80
81
    /** @var array Assets path collection */
82
    protected $assets = [];
83
84
    /** @var array Menus collection. */
85
    protected $menus;
86
87
    /** @var array Taxonomies collection. */
88
    protected $taxonomies;
89
90
    /** @var Renderer\RendererInterface Renderer. */
91
    protected $renderer;
92
93
    /** @var GeneratorManager Generators manager. */
94
    protected $generatorManager;
95
96
    /** @var string Application version. */
97
    protected static $version;
98
99
    /** @var array Build metrics. */
100
    protected $metrics = [];
101
102
    /** @var string curent build ID */
103
    protected $buildId;
104
105
    /**
106
     * @param Config|array|null    $config
107
     * @param LoggerInterface|null $logger
108
     */
109 1
    public function __construct($config = null, ?LoggerInterface $logger = null)
110
    {
111
        // init and set config
112 1
        $this->config = new Config();
113 1
        if ($config !== null) {
114 1
            $this->setConfig($config);
115
        }
116
        // debug mode?
117 1
        if (getenv('CECIL_DEBUG') == 'true' || $this->getConfig()->isEnabled('debug')) {
118 1
            $this->debug = true;
119
        }
120
        // set logger
121 1
        if ($logger === null) {
122
            $logger = new PrintLogger(self::VERBOSITY_VERBOSE);
123
        }
124 1
        $this->setLogger($logger);
125
    }
126
127
    /**
128
     * Creates a new Builder instance.
129
     */
130 1
    public static function create(): self
131
    {
132 1
        $class = new \ReflectionClass(\get_called_class());
133
134 1
        return $class->newInstanceArgs(\func_get_args());
135
    }
136
137
    /**
138
     * Builds a new website.
139
     */
140 1
    public function build(array $options): self
141
    {
142
        // set start script time and memory usage
143 1
        $startTime = microtime(true);
144 1
        $startMemory = memory_get_usage();
145
146
        // log warnings
147 1
        $this->logBuildWarnings();
148
149
        // prepare options
150 1
        $this->options = array_merge([
151 1
            'drafts'  => false, // build drafts or not
152 1
            'dry-run' => false, // if dry-run is true, generated files are not saved
153 1
            'page'    => '',    // specific page to build
154 1
        ], $options);
155
156
        // set build ID
157 1
        $this->buildId = date('YmdHis');
158
159
        // process each step
160 1
        $steps = [];
161
        // init...
162 1
        foreach ($this->steps as $step) {
163
            /** @var Step\StepInterface $stepObject */
164 1
            $stepObject = new $step($this);
165 1
            $stepObject->init($this->options);
166 1
            if ($stepObject->canProcess()) {
167 1
                $steps[] = $stepObject;
168
            }
169
        }
170
        // ...and process!
171 1
        $stepNumber = 0;
172 1
        $stepsTotal = \count($steps);
173 1
        foreach ($steps as $step) {
174 1
            $stepNumber++;
175
            /** @var Step\StepInterface $step */
176 1
            $this->getLogger()->notice($step->getName(), ['step' => [$stepNumber, $stepsTotal]]);
177 1
            $stepStartTime = microtime(true);
178 1
            $stepStartMemory = memory_get_usage();
179 1
            $step->process();
180
            // step duration and memory usage
181 1
            $this->metrics['steps'][$stepNumber]['name'] = $step->getName();
182 1
            $this->metrics['steps'][$stepNumber]['duration'] = Util::convertMicrotime((float) $stepStartTime);
183 1
            $this->metrics['steps'][$stepNumber]['memory']   = Util::convertMemory(memory_get_usage() - $stepStartMemory);
184 1
            $this->getLogger()->info(\sprintf(
185 1
                '%s done in %s (%s)',
186 1
                $this->metrics['steps'][$stepNumber]['name'],
187 1
                $this->metrics['steps'][$stepNumber]['duration'],
188 1
                $this->metrics['steps'][$stepNumber]['memory']
189 1
            ));
190
        }
191
        // build duration and memory usage
192 1
        $this->metrics['total']['duration'] = Util::convertMicrotime($startTime);
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

192
        $this->metrics['total']['duration'] = Util::convertMicrotime(/** @scrutinizer ignore-type */ $startTime);
Loading history...
193 1
        $this->metrics['total']['memory']   = Util::convertMemory(memory_get_usage() - $startMemory);
194 1
        $this->getLogger()->notice(\sprintf('Built in %s (%s)', $this->metrics['total']['duration'], $this->metrics['total']['memory']));
195
196 1
        return $this;
197
    }
198
199
    /**
200
     * Returns current build ID.
201
     */
202 1
    public function getBuilId(): string
203
    {
204 1
        return $this->buildId;
205
    }
206
207
    /**
208
     * Set configuration.
209
     */
210 1
    public function setConfig(array|Config $config): self
211
    {
212 1
        if (\is_array($config)) {
213 1
            $config = new Config($config);
214
        }
215 1
        if ($this->config !== $config) {
216 1
            $this->config = $config;
217
        }
218
219
        // import themes configuration
220 1
        $this->importThemesConfig();
221
        // autoloads local extensions
222 1
        Util::autoload($this, 'extensions');
223
224 1
        return $this;
225
    }
226
227
    /**
228
     * Returns configuration.
229
     */
230 1
    public function getConfig(): Config
231
    {
232 1
        if ($this->config === null) {
233
            $this->config = new Config();
234
        }
235
236 1
        return $this->config;
237
    }
238
239
    /**
240
     * Config::setSourceDir() alias.
241
     */
242 1
    public function setSourceDir(string $sourceDir): self
243
    {
244 1
        $this->getConfig()->setSourceDir($sourceDir);
245
        // import themes configuration
246 1
        $this->importThemesConfig();
247
248 1
        return $this;
249
    }
250
251
    /**
252
     * Config::setDestinationDir() alias.
253
     */
254 1
    public function setDestinationDir(string $destinationDir): self
255
    {
256 1
        $this->getConfig()->setDestinationDir($destinationDir);
257
258 1
        return $this;
259
    }
260
261
    /**
262
     * Import themes configuration.
263
     */
264 1
    public function importThemesConfig(): void
265
    {
266 1
        foreach ($this->config->get('theme') as $theme) {
267 1
            $this->config->import(Config::loadFile(Util::joinFile($this->config->getThemesPath(), $theme, 'config.yml'), true), Config::PRESERVE);
268
        }
269
    }
270
271
    /**
272
     * {@inheritdoc}
273
     */
274 1
    public function setLogger(LoggerInterface $logger): void
275
    {
276 1
        $this->logger = $logger;
277
    }
278
279
    /**
280
     * Returns the logger instance.
281
     */
282 1
    public function getLogger(): LoggerInterface
283
    {
284 1
        return $this->logger;
285
    }
286
287
    /**
288
     * Returns debug mode state.
289
     */
290 1
    public function isDebug(): bool
291
    {
292 1
        return (bool) $this->debug;
293
    }
294
295
    /**
296
     * Returns build options.
297
     */
298 1
    public function getBuildOptions(): array
299
    {
300 1
        return $this->options;
301
    }
302
303
    /**
304
     * Set collected pages files.
305
     */
306 1
    public function setPagesFiles(Finder $content): void
307
    {
308 1
        $this->content = $content;
309
    }
310
311
    /**
312
     * Returns pages files.
313
     */
314 1
    public function getPagesFiles(): ?Finder
315
    {
316 1
        return $this->content;
317
    }
318
319
    /**
320
     * Set collected data.
321
     */
322 1
    public function setData(array $data): void
323
    {
324 1
        $this->data = $data;
325
    }
326
327
    /**
328
     * Returns data collection.
329
     */
330 1
    public function getData(?string $language = null): ?array
331
    {
332 1
        if ($language) {
333 1
            if (empty($this->data[$language])) {
334
                // fallback to default language
335 1
                return $this->data[$this->config->getLanguageDefault()];
336
            }
337
338 1
            return $this->data[$language];
339
        }
340
341 1
        return $this->data;
342
    }
343
344
    /**
345
     * Set collected static files.
346
     */
347 1
    public function setStatic(array $static): void
348
    {
349 1
        $this->static = $static;
350
    }
351
352
    /**
353
     * Returns static files collection.
354
     */
355 1
    public function getStatic(): array
356
    {
357 1
        return $this->static;
358
    }
359
360
    /**
361
     * Set/update Pages collection.
362
     */
363 1
    public function setPages(PagesCollection $pages): void
364
    {
365 1
        $this->pages = $pages;
366
    }
367
368
    /**
369
     * Returns pages collection.
370
     */
371 1
    public function getPages(): ?PagesCollection
372
    {
373 1
        return $this->pages;
374
    }
375
376
    /**
377
     * Set assets path list.
378
     */
379
    public function setAssets(array $assets): void
380
    {
381
        $this->assets = $assets;
382
    }
383
384
    /**
385
     * Add an asset path to assets list.
386
     */
387 1
    public function addAsset(string $path): void
388
    {
389 1
        if (!\in_array($path, $this->assets, true)) {
390 1
            $this->assets[] = $path;
391
        }
392
    }
393
394
    /**
395
     * Returns list of assets path.
396
     */
397 1
    public function getAssets(): ?array
398
    {
399 1
        return $this->assets;
400
    }
401
402
    /**
403
     * Set menus collection.
404
     */
405 1
    public function setMenus(array $menus): void
406
    {
407 1
        $this->menus = $menus;
408
    }
409
410
    /**
411
     * Returns all menus, for a language.
412
     */
413 1
    public function getMenus(string $language): Collection\Menu\Collection
414
    {
415 1
        return $this->menus[$language];
416
    }
417
418
    /**
419
     * Set taxonomies collection.
420
     */
421 1
    public function setTaxonomies(array $taxonomies): void
422
    {
423 1
        $this->taxonomies = $taxonomies;
424
    }
425
426
    /**
427
     * Returns taxonomies collection, for a language.
428
     */
429 1
    public function getTaxonomies(string $language): ?Collection\Taxonomy\Collection
430
    {
431 1
        return $this->taxonomies[$language];
432
    }
433
434
    /**
435
     * Set renderer object.
436
     */
437 1
    public function setRenderer(Renderer\RendererInterface $renderer): void
438
    {
439 1
        $this->renderer = $renderer;
440
    }
441
442
    /**
443
     * Returns Renderer object.
444
     */
445 1
    public function getRenderer(): Renderer\RendererInterface
446
    {
447 1
        return $this->renderer;
448
    }
449
450
    /**
451
     * Returns metrics array.
452
     */
453
    public function getMetrics(): array
454
    {
455
        return $this->metrics;
456
    }
457
458
    /**
459
     * Returns application version.
460
     *
461
     * @throws RuntimeException
462
     */
463 1
    public static function getVersion(): string
464
    {
465 1
        if (!isset(self::$version)) {
466
            try {
467 1
                $filePath = Util\File::getRealPath('VERSION');
468
                $version = Util\File::fileGetContents($filePath);
469
                if ($version === false) {
470
                    throw new RuntimeException(\sprintf('Can\'t read content of "%s".', $filePath));
471
                }
472
                self::$version = trim($version);
473 1
            } catch (\Exception) {
474 1
                self::$version = self::VERSION;
475
            }
476
        }
477
478 1
        return self::$version;
479
    }
480
481
    /**
482
     * Log build warnings.
483
     */
484 1
    protected function logBuildWarnings(): void
485
    {
486
        // baseurl
487 1
        if (empty(trim((string) $this->config->get('baseurl'), '/'))) {
488
            $this->getLogger()->warning('`baseurl` configuration key is required in production.');
489
        }
490
    }
491
}
492