Completed
Push — master ( 3442f8...f4ff57 )
by Vladimir
02:32
created

Website::watch()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 47
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 28
nc 1
nop 0
dl 0
loc 47
rs 8.6845
c 0
b 0
f 0
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;
9
10
use allejo\stakx\Core\StakxLogger;
11
use allejo\stakx\Manager\AssetManager;
12
use allejo\stakx\Manager\CollectionManager;
13
use allejo\stakx\Manager\DataManager;
14
use allejo\stakx\Manager\MenuManager;
15
use allejo\stakx\Manager\PageManager;
16
use allejo\stakx\Manager\ThemeManager;
17
use allejo\stakx\Manager\TwigManager;
18
use allejo\stakx\System\FileExplorer;
19
use allejo\stakx\System\Filesystem;
20
use allejo\stakx\System\Folder;
21
use JasonLewis\ResourceWatcher\Event;
22
use JasonLewis\ResourceWatcher\Resource\FileResource;
23
use JasonLewis\ResourceWatcher\Tracker;
24
use JasonLewis\ResourceWatcher\Watcher;
25
use Symfony\Component\Console\Output\OutputInterface;
26
27
class Website
28
{
29
    /**
30
     * The location of where the compiled website will be written to.
31
     *
32
     * @var Folder
33
     */
34
    private $outputDirectory;
35
36
    /**
37
     * The main configuration to be used to build the specified website.
38
     *
39
     * @var Configuration
40
     */
41
    private $configuration;
42
43
    /**
44
     * When set to true, the Stakx website will be built without a configuration file.
45
     *
46
     * @var bool
47
     */
48
    private $confLess;
49
50
    /**
51
     * When set to true, Twig templates will not have access to filters or functions which provide access to the
52
     * filesystem.
53
     *
54
     * @var bool
55
     */
56
    private $safeMode;
57
58
    /**
59
     * When set to true, Stakx will not clean the _site folder after a rebuild.
60
     *
61
     * @var bool
62
     */
63
    private $noClean;
64
65
    /**
66
     * @var StakxLogger
67
     */
68
    private $output;
69
70
    /**
71
     * @var AssetManager
72
     */
73
    private $am;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $am. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
74
75
    /**
76
     * @var CollectionManager
77
     */
78
    private $cm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $cm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
79
80
    /**
81
     * @var DataManager
82
     */
83
    private $dm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
84
85
    /**
86
     * @var Filesystem
87
     */
88
    private $fs;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
89
90
    /** @var MenuManager */
91
    private $mm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $mm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
92
93
    /**
94
     * @var PageManager
95
     */
96
    private $pm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $pm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
97
98
    /**
99
     * @var ThemeManager
100
     */
101
    private $tm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $tm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
102
103
    /** @var Compiler */
104
    private $compiler;
105
106
    /**
107
     * Website constructor.
108
     *
109
     * @param OutputInterface $output
110
     */
111
    public function __construct(OutputInterface $output)
112
    {
113
        $this->output = new StakxLogger($output);
114
        $this->cm = new CollectionManager();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
115
        $this->dm = new DataManager();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
116
        $this->mm = new MenuManager();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
117
        $this->pm = new PageManager();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
118
        $this->fs = new Filesystem();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
119
    }
120
121
    /**
122
     * Compile the website.
123
     *
124
     * @param bool $tracking Whether or not to keep track of files as they're compiled to save time in 'watch'
125
     */
126
    public function build($tracking = false)
127
    {
128
        // Configure the environment
129
        $this->createFolderStructure(!$this->noClean);
130
131
        // Our output directory
132
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
133
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
134
135
        // Parse DataItems
136
        $this->dm->setLogger($this->output);
137
        $this->dm->enableTracking($tracking);
138
        $this->dm->parseDataItems($this->getConfiguration()->getDataFolders());
139
        $this->dm->parseDataSets($this->getConfiguration()->getDataSets());
140
141
        // Prepare Collections
142
        $this->cm->setLogger($this->output);
143
        $this->cm->enableTracking($tracking);
144
        $this->cm->parseCollections($this->getConfiguration()->getCollectionsFolders());
145
146
        // Handle PageViews
147
        $this->pm->setLogger($this->output);
148
        $this->pm->enableTracking($tracking);
149
        $this->pm->setCollections($this->cm->getCollections());
150
        $this->pm->parsePageViews($this->getConfiguration()->getPageViewFolders());
151
152
        // Handle the site's menu
153
        $this->mm->setLogger($this->output);
154
        $this->mm->buildFromPageViews($this->pm->getStaticPageViews());
155
156
        // Configure our Twig environment
157
        $twigEnv = new TwigManager();
158
        $twigEnv->configureTwig($this->getConfiguration(), array(
159
            'safe'    => $this->safeMode,
160
            'globals' => array(
161
                array('name' => 'site', 'value' => $this->getConfiguration()->getConfiguration()),
162
                array('name' => 'collections', 'value' => $this->cm->getJailedCollections()),
163
                array('name' => 'menu', 'value' => $this->mm->getSiteMenu()),
164
                array('name' => 'pages', 'value' => $this->pm->getJailedStaticPageViews()),
165
                array('name' => 'data', 'value' => $this->dm->getDataItems()),
166
            ),
167
        ));
168
169
        // Compile everything
170
        $this->compiler = new Compiler();
171
        $this->compiler->setLogger($this->output);
172
        $this->compiler->setRedirectTemplate($this->getConfiguration()->getRedirectTemplate());
173
        $this->compiler->setPageViews($this->pm->getPageViews(), $this->pm->getPageViewsFlattened());
174
        $this->compiler->setTargetFolder($this->outputDirectory);
175
        $this->compiler->compileAll();
176
177
        // At this point, we are looking at static files to copy over meaning we need to ignore all of the files that
178
        // make up the source of a stakx website
179
        $assetsToIgnore = array_merge(
180
            Configuration::$stakxSourceFiles,
181
            $this->getConfiguration()->getExcludes()
182
        );
183
184
        //
185
        // Theme Management
186
        //
187
        $theme = $this->configuration->getTheme();
188
189
        if (!is_null($theme))
190
        {
191
            $this->output->notice("Looking for '${theme}' theme...");
192
193
            $this->tm = new ThemeManager($theme);
194
            $this->tm->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
195
            $this->tm->setLogger($this->output);
196
            $this->tm->enableTracking($tracking);
197
            $this->tm->setFolder($this->outputDirectory);
198
            $this->tm->copyFiles();
199
        }
200
201
        //
202
        // Static file management
203
        //
204
        $this->am = new AssetManager();
205
        $this->am->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
206
        $this->am->setLogger($this->output);
207
        $this->am->setFolder($this->outputDirectory);
208
        $this->am->enableTracking($tracking);
209
        $this->am->copyFiles();
210
    }
211
212
    public function watch()
213
    {
214
        $this->output->writeln('Building website...');
215
        $this->build(true);
216
        $this->output->writeln(sprintf('Watching %s', getcwd()));
217
218
        $fileExplorer = FileExplorer::create(
219
            getcwd(),
220
            array_merge($this->getConfiguration()->getExcludes(), array(
221
                $this->getConfiguration()->getTargetFolder(),
222
            )),
223
            $this->getConfiguration()->getIncludes()
224
        );
225
        $tracker = new Tracker();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
226
        $watcher = new Watcher($tracker, $this->fs);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
227
        $listener = $watcher->watch(getcwd(), $fileExplorer->getExplorer());
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
228
        $targetPath = $this->getConfiguration()->getTargetFolder();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
229
230
        $this->output->writeln('Watch started successfully');
231
232
        $listener->onAnything(function (Event $event, FileResource $resouce, $path) use ($targetPath)
233
        {
234
            $filePath = $this->fs->getRelativePath($path);
235
236
            try
237
            {
238
                switch ($event->getCode())
239
                {
240
                    case Event::RESOURCE_CREATED:
241
                        $this->creationWatcher($filePath);
242
                        break;
243
244
                    case Event::RESOURCE_MODIFIED:
245
                        $this->modificationWatcher($filePath);
246
                        break;
247
                }
248
            }
249
            catch (\Exception $e)
250
            {
251
                $this->output->error(sprintf('Your website failed to build with the following error: %s',
252
                    $e->getMessage()
253
                ));
254
            }
255
        });
256
257
        $watcher->start();
258
    }
259
260
    /**
261
     * @return Configuration
262
     */
263
    public function getConfiguration()
264
    {
265
        return $this->configuration;
266
    }
267
268
    /**
269
     * @param string $configFile
270
     *
271
     * @throws \LogicException
272
     */
273
    public function setConfiguration($configFile)
274
    {
275
        if (!$this->fs->exists($configFile) && !$this->isConfLess())
276
        {
277
            $this->output->error('You are trying to build a website in a directory without a configuration file. Is this what you meant to do?');
278
            $this->output->error("To build a website without a configuration, use the '--no-conf' option");
279
280
            throw new \LogicException('Cannot build a website without a configuration when not in Configuration-less mode');
281
        }
282
283
        if ($this->isConfLess())
284
        {
285
            $configFile = '';
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $configFile. This often makes code more readable.
Loading history...
286
        }
287
288
        $this->configuration = new Configuration();
289
        $this->configuration->setLogger($this->output);
290
        $this->configuration->parseConfiguration($configFile);
291
    }
292
293
    /**
294
     * Get whether or not the website is being built in Configuration-less mode.
295
     *
296
     * @return bool True when being built with no configuration file
297
     */
298
    public function isConfLess()
299
    {
300
        return $this->confLess;
301
    }
302
303
    /**
304
     * Set whether or not the website should be built with a configuration.
305
     *
306
     * @param bool $status True when a website should be built without a configuration
307
     */
308
    public function setConfLess($status)
309
    {
310
        $this->confLess = $status;
311
    }
312
313
    /**
314
     * Get whether or not the website is being built in safe mode.
315
     *
316
     * Safe mode is defined as disabling file system access from Twig and disabling user Twig extensions
317
     *
318
     * @return bool True when the website is being built in safe mode
319
     */
320
    public function isSafeMode()
321
    {
322
        return $this->safeMode;
323
    }
324
325
    /**
326
     * Set whether a website should be built in safe mode.
327
     *
328
     * @param bool $bool True if a website should be built in safe mode
329
     */
330
    public function setSafeMode($bool)
331
    {
332
        $this->safeMode = $bool;
333
    }
334
335
    /**
336
     * @return bool
337
     */
338
    public function isNoClean()
339
    {
340
        return $this->noClean;
341
    }
342
343
    /**
344
     * @param bool $noClean
345
     */
346
    public function setNoClean($noClean)
347
    {
348
        $this->noClean = $noClean;
349
    }
350
351
    private function creationWatcher($filePath)
352
    {
353
        $this->output->writeln(sprintf('File creation detected: %s', $filePath));
354
355
        if ($this->pm->isHandled($filePath))
356
        {
357
            $this->pm->createNewItem($filePath);
358
            $this->pm->refreshItem($filePath);
359
        }
360
        elseif ($this->cm->isHandled($filePath))
361
        {
362
            $contentItem = $this->cm->createNewItem($filePath);
363
            TwigManager::getInstance()->addGlobal('collections', $this->cm->getCollections());
364
365
            $this->pm->trackNewContentItem($contentItem);
366
            $this->compiler->compileContentItem($contentItem);
0 ignored issues
show
Deprecated Code introduced by
The method allejo\stakx\Compiler::compileContentItem() has been deprecated.

This method has been deprecated.

Loading history...
367
            $this->compiler->compileSome(array(
368
                'namespace'  => 'collections',
369
                'dependency' => $contentItem->getCollection(),
370
            ));
371
        }
372 View Code Duplication
        elseif ($this->dm->isHandled($filePath))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
        {
374
            $change = $this->dm->createNewItem($filePath);
375
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
376
377
            $this->compiler->compileSome(array(
378
                'namespace'  => 'data',
379
                'dependency' => $change,
380
            ));
381
        }
382
        elseif (!is_null($this->tm) && $this->tm->isHandled($filePath))
383
        {
384
            $this->tm->createNewItem($filePath);
385
        }
386
        elseif ($this->am->isHandled($filePath))
387
        {
388
            $this->am->createNewItem($filePath);
389
        }
390
    }
391
392
    private function modificationWatcher($filePath)
393
    {
394
        $this->output->writeln(sprintf('File change detected: %s', $filePath));
395
396
        if ($this->pm->isTracked($filePath))
397
        {
398
            $this->pm->refreshItem($filePath);
399
        }
400
        elseif ($this->cm->isTracked($filePath))
401
        {
402
            $contentItem = &$this->cm->getContentItem($filePath);
403
            $contentItem->refreshFileContent();
404
405
            $this->compiler->compileContentItem($contentItem);
0 ignored issues
show
Deprecated Code introduced by
The method allejo\stakx\Compiler::compileContentItem() has been deprecated.

This method has been deprecated.

Loading history...
406
            $this->compiler->compileSome(array(
407
                'namespace'  => 'collections',
408
                'dependency' => $contentItem->getCollection(),
409
            ));
410
        }
411 View Code Duplication
        elseif ($this->dm->isTracked($filePath))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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