Completed
Pull Request — master (#41)
by Vladimir
02:31
created

Website   C

Complexity

Total Complexity 34

Size/Duplication

Total Lines 423
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 423
rs 6.7466
wmc 34
lcom 1
cbo 18

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
B build() 0 85 2
B watch() 0 47 4
A getConfiguration() 0 4 1
A setConfiguration() 0 19 4
A isConfLess() 0 4 1
A setConfLess() 0 4 1
A isSafeMode() 0 4 1
A setSafeMode() 0 4 1
A isNoClean() 0 4 1
A setNoClean() 0 4 1
C creationWatcher() 0 40 7
C modificationWatcher() 0 38 7
A createFolderStructure() 0 13 2
1
<?php
2
3
/**
4
 * @copyright 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Object;
9
10
use allejo\stakx\Compiler;
11
use allejo\stakx\Core\StakxLogger;
12
use allejo\stakx\Manager\AssetManager;
13
use allejo\stakx\Manager\CollectionManager;
14
use allejo\stakx\Manager\DataManager;
15
use allejo\stakx\Manager\MenuManager;
16
use allejo\stakx\Manager\PageManager;
17
use allejo\stakx\Manager\ThemeManager;
18
use allejo\stakx\Manager\TwigManager;
19
use allejo\stakx\System\FileExplorer;
20
use allejo\stakx\System\Filesystem;
21
use allejo\stakx\System\Folder;
22
use JasonLewis\ResourceWatcher\Event;
23
use JasonLewis\ResourceWatcher\Resource\FileResource;
24
use JasonLewis\ResourceWatcher\Tracker;
25
use JasonLewis\ResourceWatcher\Watcher;
26
use Symfony\Component\Console\Output\OutputInterface;
27
28
class Website
29
{
30
    /**
31
     * The location of where the compiled website will be written to.
32
     *
33
     * @var Folder
34
     */
35
    private $outputDirectory;
36
37
    /**
38
     * The main configuration to be used to build the specified website.
39
     *
40
     * @var Configuration
41
     */
42
    private $configuration;
43
44
    /**
45
     * When set to true, the Stakx website will be built without a configuration file.
46
     *
47
     * @var bool
48
     */
49
    private $confLess;
50
51
    /**
52
     * When set to true, Twig templates will not have access to filters or functions which provide access to the
53
     * filesystem.
54
     *
55
     * @var bool
56
     */
57
    private $safeMode;
58
59
    /**
60
     * When set to true, Stakx will not clean the _site folder after a rebuild.
61
     *
62
     * @var bool
63
     */
64
    private $noClean;
65
66
    /**
67
     * @var StakxLogger
68
     */
69
    private $output;
70
71
    /**
72
     * @var AssetManager
73
     */
74
    private $am;
75
76
    /**
77
     * @var CollectionManager
78
     */
79
    private $cm;
80
81
    /**
82
     * @var DataManager
83
     */
84
    private $dm;
85
86
    /**
87
     * @var Filesystem
88
     */
89
    private $fs;
90
91
    /** @var MenuManager */
92
    private $mm;
93
94
    /**
95
     * @var PageManager
96
     */
97
    private $pm;
98
99
    /**
100
     * @var ThemeManager
101
     */
102
    private $tm;
103
104
    /** @var Compiler */
105
    private $compiler;
106
107
    /**
108
     * Website constructor.
109
     *
110
     * @param OutputInterface $output
111
     */
112
    public function __construct(OutputInterface $output)
113
    {
114
        $this->output = new StakxLogger($output);
115
        $this->cm = new CollectionManager();
116
        $this->dm = new DataManager();
117
        $this->mm = new MenuManager();
118
        $this->pm = new PageManager();
119
        $this->fs = new Filesystem();
120
    }
121
122
    /**
123
     * Compile the website.
124
     *
125
     * @param bool $tracking Whether or not to keep track of files as they're compiled to save time in 'watch'
126
     */
127
    public function build($tracking = false)
128
    {
129
        // Configure the environment
130
        $this->createFolderStructure(!$this->noClean);
131
132
        // Our output directory
133
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
134
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
135
136
        // Parse DataItems
137
        $this->dm->setLogger($this->output);
138
        $this->dm->enableTracking($tracking);
139
        $this->dm->parseDataItems($this->getConfiguration()->getDataFolders());
140
        $this->dm->parseDataSets($this->getConfiguration()->getDataSets());
141
142
        // Prepare Collections
143
        $this->cm->setLogger($this->output);
144
        $this->cm->enableTracking($tracking);
145
        $this->cm->parseCollections($this->getConfiguration()->getCollectionsFolders());
146
147
        // Handle PageViews
148
        $this->pm->setLogger($this->output);
149
        $this->pm->enableTracking($tracking);
150
        $this->pm->setCollections($this->cm->getCollections());
151
        $this->pm->parsePageViews($this->getConfiguration()->getPageViewFolders());
152
153
        // Handle the site's menu
154
        $this->mm->setLogger($this->output);
155
        $this->mm->buildFromPageViews($this->pm->getStaticPageViews());
156
157
        // Configure our Twig environment
158
        $twigEnv = new TwigManager();
159
        $twigEnv->configureTwig($this->getConfiguration(), array(
160
            'safe'    => $this->safeMode,
161
            'globals' => array(
162
                array('name' => 'site', 'value' => $this->getConfiguration()->getConfiguration()),
163
                array('name' => 'collections', 'value' => $this->cm->getJailedCollections()),
164
                array('name' => 'menu', 'value' => $this->mm->getSiteMenu()),
165
                array('name' => 'pages', 'value' => $this->pm->getJailedStaticPageViews()),
166
                array('name' => 'data', 'value' => $this->dm->getDataItems()),
167
            ),
168
        ));
169
170
        // Compile everything
171
        $this->compiler = new Compiler();
172
        $this->compiler->setLogger($this->output);
173
        $this->compiler->setRedirectTemplate($this->getConfiguration()->getRedirectTemplate());
174
        $this->compiler->setPageViews($this->pm->getPageViews());
0 ignored issues
show
Documentation introduced by
$this->pm->getPageViews() is of type array<integer,array<inte...takx\Object\PageView>>>, but the function expects a array<integer,object<all...stakx\Object\PageView>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
175
        $this->compiler->setTargetFolder($this->outputDirectory);
176
        $this->compiler->compileAll();
177
178
        // At this point, we are looking at static files to copy over meaning we need to ignore all of the files that
179
        // make up the source of a stakx website
180
        $assetsToIgnore = array_merge(
181
            Configuration::$stakxSourceFiles,
182
            $this->getConfiguration()->getExcludes()
183
        );
184
185
        //
186
        // Theme Management
187
        //
188
        $theme = $this->configuration->getTheme();
189
190
        if (!is_null($theme))
191
        {
192
            $this->output->notice("Looking for '${theme}' theme...");
193
194
            $this->tm = new ThemeManager($theme);
195
            $this->tm->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
196
            $this->tm->setLogger($this->output);
197
            $this->tm->enableTracking($tracking);
198
            $this->tm->setFolder($this->outputDirectory);
199
            $this->tm->copyFiles();
200
        }
201
202
        //
203
        // Static file management
204
        //
205
        $this->am = new AssetManager();
206
        $this->am->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
207
        $this->am->setLogger($this->output);
208
        $this->am->setFolder($this->outputDirectory);
209
        $this->am->enableTracking($tracking);
210
        $this->am->copyFiles();
211
    }
212
213
    public function watch()
214
    {
215
        $this->output->writeln('Building website...');
216
        $this->build(true);
217
        $this->output->writeln(sprintf('Watching %s', getcwd()));
218
219
        $fileExplorer = FileExplorer::create(
220
            getcwd(),
221
            array_merge($this->getConfiguration()->getExcludes(), array(
222
                $this->getConfiguration()->getTargetFolder(),
223
            )),
224
            $this->getConfiguration()->getIncludes()
225
        );
226
        $tracker = new Tracker();
227
        $watcher = new Watcher($tracker, $this->fs);
228
        $listener = $watcher->watch(getcwd(), $fileExplorer->getExplorer());
229
        $targetPath = $this->getConfiguration()->getTargetFolder();
230
231
        $this->output->writeln('Watch started successfully');
232
233
        $listener->onAnything(function (Event $event, FileResource $resouce, $path) use ($targetPath)
234
        {
235
            $filePath = $this->fs->getRelativePath($path);
236
237
            try
238
            {
239
                switch ($event->getCode())
240
                {
241
                    case Event::RESOURCE_CREATED:
242
                        $this->creationWatcher($filePath);
243
                        break;
244
245
                    case Event::RESOURCE_MODIFIED:
246
                        $this->modificationWatcher($filePath);
247
                        break;
248
                }
249
            }
250
            catch (\Exception $e)
251
            {
252
                $this->output->error(sprintf('Your website failed to build with the following error: %s',
253
                    $e->getMessage()
254
                ));
255
            }
256
        });
257
258
        $watcher->start();
259
    }
260
261
    /**
262
     * @return Configuration
263
     */
264
    public function getConfiguration()
265
    {
266
        return $this->configuration;
267
    }
268
269
    /**
270
     * @param string $configFile
271
     *
272
     * @throws \LogicException
273
     */
274
    public function setConfiguration($configFile)
275
    {
276
        if (!$this->fs->exists($configFile) && !$this->isConfLess())
277
        {
278
            $this->output->error('You are trying to build a website in a directory without a configuration file. Is this what you meant to do?');
279
            $this->output->error("To build a website without a configuration, use the '--no-conf' option");
280
281
            throw new \LogicException('Cannot build a website without a configuration when not in Configuration-less mode');
282
        }
283
284
        if ($this->isConfLess())
285
        {
286
            $configFile = '';
287
        }
288
289
        $this->configuration = new Configuration();
290
        $this->configuration->setLogger($this->output);
291
        $this->configuration->parseConfiguration($configFile);
292
    }
293
294
    /**
295
     * Get whether or not the website is being built in Configuration-less mode.
296
     *
297
     * @return bool True when being built with no configuration file
298
     */
299
    public function isConfLess()
300
    {
301
        return $this->confLess;
302
    }
303
304
    /**
305
     * Set whether or not the website should be built with a configuration.
306
     *
307
     * @param bool $status True when a website should be built without a configuration
308
     */
309
    public function setConfLess($status)
310
    {
311
        $this->confLess = $status;
312
    }
313
314
    /**
315
     * Get whether or not the website is being built in safe mode.
316
     *
317
     * Safe mode is defined as disabling file system access from Twig and disabling user Twig extensions
318
     *
319
     * @return bool True when the website is being built in safe mode
320
     */
321
    public function isSafeMode()
322
    {
323
        return $this->safeMode;
324
    }
325
326
    /**
327
     * Set whether a website should be built in safe mode.
328
     *
329
     * @param bool $bool True if a website should be built in safe mode
330
     */
331
    public function setSafeMode($bool)
332
    {
333
        $this->safeMode = $bool;
334
    }
335
336
    /**
337
     * @return bool
338
     */
339
    public function isNoClean()
340
    {
341
        return $this->noClean;
342
    }
343
344
    /**
345
     * @param bool $noClean
346
     */
347
    public function setNoClean($noClean)
348
    {
349
        $this->noClean = $noClean;
350
    }
351
352
    private function creationWatcher($filePath)
353
    {
354
        $this->output->writeln(sprintf('File creation detected: %s', $filePath));
355
356
        if ($this->pm->isHandled($filePath))
357
        {
358
            $this->pm->createNewItem($filePath);
359
            $this->pm->refreshItem($filePath);
360
        }
361
        elseif ($this->cm->isHandled($filePath))
362
        {
363
            $contentItem = $this->cm->createNewItem($filePath);
364
365
            $this->pm->updateTwigVariable('collections', $this->cm->getCollections());
0 ignored issues
show
Bug introduced by
The method updateTwigVariable() does not seem to exist on object<allejo\stakx\Manager\PageManager>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
366
            $this->pm->trackNewContentItem($contentItem);
367
//            $this->pm->compileContentItem($contentItem);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
368
//            $this->pm->compileSome(array(
369
//                'namespace' => 'collections',
370
//                'dependency' => $contentItem->getCollection()
371
//            ));
372
        }
373
        elseif ($this->dm->isHandled($filePath))
374
        {
375
            $change = $this->dm->createNewItem($filePath);
0 ignored issues
show
Unused Code introduced by
$change is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
376
377
            $this->pm->updateTwigVariable('data', $this->dm->getDataItems());
0 ignored issues
show
Bug introduced by
The method updateTwigVariable() does not seem to exist on object<allejo\stakx\Manager\PageManager>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
378
//            $this->pm->compileSome(array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
379
//                'namespace' => 'data',
380
//                'dependency' => $change
381
//            ));
382
        }
383
        elseif (!is_null($this->tm) && $this->tm->isHandled($filePath))
384
        {
385
            $this->tm->createNewItem($filePath);
386
        }
387
        elseif ($this->am->isHandled($filePath))
388
        {
389
            $this->am->createNewItem($filePath);
390
        }
391
    }
392
393
    private function modificationWatcher($filePath)
394
    {
395
        $this->output->writeln(sprintf('File change detected: %s', $filePath));
396
397
        if ($this->pm->isTracked($filePath))
398
        {
399
            $this->pm->refreshItem($filePath);
400
        }
401
        elseif ($this->cm->isTracked($filePath))
402
        {
403
            $contentItem = &$this->cm->getContentItem($filePath);
404
            $contentItem->refreshFileContent();
405
406
//            $this->pm->compileContentItem($contentItem);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
407
//            $this->pm->compileSome(array(
408
//                'namespace' => 'collections',
409
//                'dependency' => $contentItem->getCollection()
410
//            ));
411
        }
412
        elseif ($this->dm->isTracked($filePath))
413
        {
414
            $change = $this->dm->refreshItem($filePath);
0 ignored issues
show
Unused Code introduced by
$change is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
415
416
//            $this->pm->updateTwigVariable('data', $this->dm->getDataItems());
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
417
//            $this->pm->compileSome(array(
418
//                'namespace' => 'data',
419
//                'dependency' => $change
420
//            ));
421
        }
422
        elseif (!is_null($this->tm) && $this->tm->isTracked($filePath))
423
        {
424
            $this->tm->refreshItem($filePath);
425
        }
426
        elseif ($this->am->isTracked($filePath))
427
        {
428
            $this->am->refreshItem($filePath);
429
        }
430
    }
431
432
    /**
433
     * Prepare the Stakx environment by creating necessary cache folders.
434
     *
435
     * @param bool $cleanDirectory Clean the target directory
436
     */
437
    private function createFolderStructure($cleanDirectory)
438
    {
439
        $tarDir = $this->fs->absolutePath($this->configuration->getTargetFolder());
440
441
        if ($cleanDirectory)
442
        {
443
            $this->fs->remove($tarDir);
444
        }
445
446
        $this->fs->remove($this->fs->absolutePath('.stakx-cache'));
447
        $this->fs->mkdir($this->fs->absolutePath('.stakx-cache/twig'));
448
        $this->fs->mkdir($tarDir);
449
    }
450
}
451