Passed
Push — master ( 9125d2...ca14c1 )
by Arnaud
05:37
created

Builder::validConfig()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 8.304

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 6
eloc 9
c 1
b 1
f 0
nc 10
nop 0
dl 0
loc 17
ccs 6
cts 10
cp 0.6
crap 8.304
rs 9.2222
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 = '7.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\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
    /**
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
    }
115
116
    /**
117
     * Creates a new Builder instance.
118
     */
119 1
    public static function create(): self
120
    {
121 1
        $class = new \ReflectionClass(get_called_class());
122
123 1
        return $class->newInstanceArgs(func_get_args());
124
    }
125
126
    /**
127
     * Builds a new website.
128
     */
129 1
    public function build(array $options): self
130
    {
131
        // set start script time and memory usage
132 1
        $startTime = microtime(true);
133 1
        $startMemory = memory_get_usage();
134
135
        // checks the configuration
136 1
        $this->validConfig();
137
138
        // prepare options
139 1
        $this->options = array_merge([
140 1
            'drafts'  => false, // build drafts or not
141 1
            'dry-run' => false, // if dry-run is true, generated files are not saved
142 1
            'page'    => '',    // specific page to build
143 1
        ], $options);
144
145
        // process each step
146 1
        $steps = [];
147
        // init...
148 1
        foreach ($this->steps as $step) {
149
            /** @var Step\StepInterface $stepObject */
150 1
            $stepObject = new $step($this);
151 1
            $stepObject->init($this->options);
152 1
            if ($stepObject->canProcess()) {
153 1
                $steps[] = $stepObject;
154
            }
155
        }
156
        // ...and process!
157 1
        $stepNumber = 0;
158 1
        $stepsTotal = count($steps);
159 1
        foreach ($steps as $step) {
160 1
            $stepNumber++;
161
            /** @var Step\StepInterface $step */
162 1
            $this->getLogger()->notice($step->getName(), ['step' => [$stepNumber, $stepsTotal]]);
163 1
            $step->process();
164
        }
165
166
        // process duration
167 1
        $message = \sprintf('Built in %s s (%s)', round(microtime(true) - $startTime, 2), Util::convertMemory(memory_get_usage() - $startMemory));
168 1
        $this->getLogger()->notice($message);
169
170 1
        return $this;
171
    }
172
173
    /**
174
     * Set configuration.
175
     *
176
     * @param Config|array|null $config
177
     */
178 1
    public function setConfig($config): self
179
    {
180 1
        if (!$config instanceof Config) {
181 1
            $config = new Config($config);
182
        }
183 1
        if ($this->config !== $config) {
184 1
            $this->config = $config;
185
        }
186
187 1
        return $this;
188
    }
189
190
    /**
191
     * Returns configuration.
192
     */
193 1
    public function getConfig(): Config
194
    {
195 1
        return $this->config;
196
    }
197
198
    /**
199
     * Config::setSourceDir() alias.
200
     */
201 1
    public function setSourceDir(string $sourceDir = null): self
202
    {
203 1
        $this->config->setSourceDir($sourceDir);
204
205 1
        return $this;
206
    }
207
208
    /**
209
     * Config::setDestinationDir() alias.
210
     */
211 1
    public function setDestinationDir(string $destinationDir = null): self
212
    {
213 1
        $this->config->setDestinationDir($destinationDir);
214
215 1
        return $this;
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221 1
    public function setLogger(LoggerInterface $logger)
222
    {
223 1
        $this->logger = $logger;
224
    }
225
226
    /**
227
     * Returns the logger instance.
228
     */
229 1
    public function getLogger(): LoggerInterface
230
    {
231 1
        return $this->logger;
232
    }
233
234
    /**
235
     * Returns debug mode state.
236
     */
237 1
    public function isDebug(): bool
238
    {
239 1
        return $this->debug;
240
    }
241
242
    /**
243
     * Returns build options.
244
     */
245 1
    public function getBuildOptions(): array
246
    {
247 1
        return $this->options;
248
    }
249
250
    /**
251
     * Set collected pages files.
252
     */
253 1
    public function setPagesFiles(Finder $content): void
254
    {
255 1
        $this->content = $content;
256
    }
257
258
    /**
259
     * Returns pages files.
260
     */
261 1
    public function getPagesFiles(): ?Finder
262
    {
263 1
        return $this->content;
264
    }
265
266
    /**
267
     * Set collected data.
268
     */
269 1
    public function setData(array $data): void
270
    {
271 1
        $this->data = $data;
272
    }
273
274
    /**
275
     * Returns data collection.
276
     */
277 1
    public function getData(): array
278
    {
279 1
        return $this->data;
280
    }
281
282
    /**
283
     * Set collected static files.
284
     */
285 1
    public function setStatic(array $static): void
286
    {
287 1
        $this->static = $static;
288
    }
289
290
    /**
291
     * Returns static files collection.
292
     */
293 1
    public function getStatic(): array
294
    {
295 1
        return $this->static;
296
    }
297
298
    /**
299
     * Set/update Pages collection.
300
     */
301 1
    public function setPages(PagesCollection $pages): void
302
    {
303 1
        $this->pages = $pages;
304
    }
305
306
    /**
307
     * Returns pages collection.
308
     */
309 1
    public function getPages(): ?PagesCollection
310
    {
311 1
        return $this->pages;
312
    }
313
314
    /**
315
     * Set menus collection.
316
     */
317 1
    public function setMenus(array $menus): void
318
    {
319 1
        $this->menus = $menus;
320
    }
321
322
    /**
323
     * Returns all menus, for a language.
324
     */
325 1
    public function getMenus(string $language): Collection\Menu\Collection
326
    {
327 1
        return $this->menus[$language];
328
    }
329
330
    /**
331
     * Set taxonomies collection.
332
     */
333 1
    public function setTaxonomies(Collection\Taxonomy\Collection $taxonomies): void
334
    {
335 1
        $this->taxonomies = $taxonomies;
336
    }
337
338
    /**
339
     * Returns taxonomies collection.
340
     */
341 1
    public function getTaxonomies(): ?Collection\Taxonomy\Collection
342
    {
343 1
        return $this->taxonomies;
344
    }
345
346
    /**
347
     * Set renderer object.
348
     */
349 1
    public function setRenderer(Renderer\RendererInterface $renderer): void
350
    {
351 1
        $this->renderer = $renderer;
352
    }
353
354
    /**
355
     * Returns Renderer object.
356
     */
357 1
    public function getRenderer(): Renderer\RendererInterface
358
    {
359 1
        return $this->renderer;
360
    }
361
362
    /**
363
     * Returns application version.
364
     *
365
     * @throws RuntimeException
366
     */
367 1
    public static function getVersion(): string
368
    {
369 1
        if (!isset(self::$version)) {
370 1
            $filePath = __DIR__ . '/../VERSION';
371 1
            if (Plateform::isPhar()) {
372
                $filePath = Plateform::getPharPath() . '/VERSION';
373
            }
374
375
            try {
376 1
                if (!file_exists($filePath)) {
377 1
                    throw new RuntimeException(\sprintf('%s file doesn\'t exist!', $filePath));
378
                }
379
                $version = Util\File::fileGetContents($filePath);
380
                if ($version === false) {
381
                    throw new RuntimeException(\sprintf('Can\'t get %s file!', $filePath));
382
                }
383
                self::$version = trim($version);
384 1
            } catch (\Exception $e) {
385 1
                self::$version = self::VERSION;
386
            }
387
        }
388
389 1
        return self::$version;
390
    }
391
392
    /**
393
     * Checks the configuration.
394
     */
395 1
    protected function validConfig(): void
396
    {
397
        // baseurl
398 1
        if (empty(trim((string) $this->config->get('baseurl'), '/'))) {
399
            $this->getLogger()->error('Config: `baseurl` is required in production (e.g.: "baseurl: https://example.com/").');
400
        }
401
        // default language
402 1
        if (!preg_match('/^' . Config::LANG_CODE_PATTERN . '$/', (string) $this->config->get('language'))) {
403
            throw new RuntimeException(\sprintf('Config: default language code "%s" is not valid (e.g.: "language: fr-FR").', $this->config->get('language')));
404
        }
405
        // locales
406 1
        foreach ((array) $this->config->get('languages') as $lang) {
407 1
            if (!isset($lang['locale'])) {
408
                throw new RuntimeException('Config: a language locale is not defined.');
409
            }
410 1
            if (!preg_match('/^' . Config::LANG_LOCALE_PATTERN . '$/', $lang['locale'])) {
411
                throw new RuntimeException(\sprintf('Config: the language locale "%s" is not valid (e.g.: "locale: fr_FR").', $lang['locale']));
412
            }
413
        }
414
    }
415
}
416