Completed
Push — master ( a6c8f9...b97be3 )
by Vladimir
03:52 queued 19s
created

Website::modificationWatcher()   C

Complexity

Conditions 10
Paths 9

Size

Total Lines 55
Code Lines 31

Duplication

Lines 10
Ratio 18.18 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 10
loc 55
rs 6.8372
cc 10
eloc 31
nc 9
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Command\BuildableCommand;
11
use allejo\stakx\Core\StakxLogger;
12
use allejo\stakx\Exception\FileAwareException;
13
use allejo\stakx\Manager\AssetManager;
14
use allejo\stakx\Manager\CollectionManager;
15
use allejo\stakx\Manager\DataManager;
16
use allejo\stakx\Manager\MenuManager;
17
use allejo\stakx\Manager\PageManager;
18
use allejo\stakx\Manager\ThemeManager;
19
use allejo\stakx\Manager\TwigManager;
20
use allejo\stakx\Filesystem\FileExplorer;
21
use allejo\stakx\System\Filesystem;
22
use allejo\stakx\Filesystem\Folder;
23
use allejo\stakx\Twig\StakxTwigTextProfiler;
24
use Highlight\Highlighter;
25
use Kwf\FileWatcher\Event\AbstractEvent;
26
use Kwf\FileWatcher\Event\Create;
27
use Kwf\FileWatcher\Event\Modify;
28
use Kwf\FileWatcher\Event\Move;
29
use Kwf\FileWatcher\Watcher;
30
use Symfony\Component\DependencyInjection\ContainerInterface;
31
32
class Website
33
{
34
    /**
35
     * The location of where the compiled website will be written to.
36
     *
37
     * @var Folder
38
     */
39
    private $outputDirectory;
40
41
    /**
42
     * The main configuration to be used to build the specified website.
43
     *
44
     * @var Configuration
45
     */
46
    private $configuration;
0 ignored issues
show
Unused Code introduced by
The property $configuration is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
47
48
    /**
49
     * When set to true, the Stakx website will be built without a configuration file.
50
     *
51
     * @var bool
52
     */
53
    private $confLess;
54
55
    /**
56
     * @var StakxLogger
57
     */
58
    private $output;
59
60
    /**
61
     * @var AssetManager
62
     */
63
    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...
64
65
    /**
66
     * @var CollectionManager
67
     */
68
    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...
69
70
    /**
71
     * @var DataManager
72
     */
73
    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...
74
75
    /**
76
     * @var Filesystem
77
     */
78
    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...
79
80
    /** @var MenuManager */
81
    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...
Unused Code introduced by
The property $mm is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
82
83
    /**
84
     * @var PageManager
85
     */
86
    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...
87
88
    /**
89
     * @var ThemeManager
90
     */
91
    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...
92
93
    /** @var Compiler */
94
    private $compiler;
95
96
    /** @var array */
97
    private $creationQueue;
98
99
    private $container;
100
101
    /**
102
     * Constructor.
103
     */
104
    public function __construct(ContainerInterface $container)
105
    {
106
        $this->container = $container;
107
108
        $this->creationQueue = array();
109
        $this->output = $container->get('logger');
110
        $this->fs = new Filesystem();
111
    }
112
113
    /**
114
     * Compile the website.
115
     *
116
     * @param bool $tracking Whether or not to keep track of files as they're compiled to save time in 'watch'
117
     */
118
    public function build($tracking = false)
119
    {
120
        $logger = $this->container->get('logger');
121
        $conf = $this->container->get(Configuration::class);
122
123
        if (empty($conf->getPageViewFolders()))
124
        {
125
            $logger->error('No PageViews were configured for this site. Check the `pageviews` key in your _config.yml.');
126
            return false;
127
        }
128
129
        Service::setParameter(BuildableCommand::WATCHING, $tracking);
130
131
        // Configure the environment
132
        $this->createFolderStructure();
133
        $this->configureHighlighter();
134
135
        // Our output directory
136
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
137
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
138
139
        $templateEngine = $this->container->get('templating');
140
141
        // Compile everything
142
        $pm = $this->container->get(PageManager::class);
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...
143
        $theme = $this->getConfiguration()->getTheme();
144
145
        $this->compiler = $this->container->get('compiler');
146
        $this->compiler->setRedirectTemplate($this->getConfiguration()->getRedirectTemplate());
147
        $this->compiler->setPageManager($pm);
148
        $this->compiler->setTargetFolder($this->outputDirectory);
149
        $this->compiler->setThemeName($theme);
150
        $this->compiler->compileAll();
151
152
        if (Service::getParameter(BuildableCommand::BUILD_PROFILE))
153
        {
154
            if (!$templateEngine->hasProfiler())
155
            {
156
                $logger->writeln('This template engine currently does not support a profiler.');
157
            }
158
            else
159
            {
160
                $profilerText = $templateEngine->getProfilerOutput($this->compiler);
161
                $logger->writeln($profilerText);
162
            }
163
        }
164
165
        // At this point, we are looking at static files to copy over meaning we need to ignore all of the files that
166
        // make up the source of a stakx website
167
        $assetsToIgnore = array_merge(
168
            Configuration::$stakxSourceFiles,
169
            $this->getConfiguration()->getExcludes()
170
        );
171
172
        //
173
        // Theme Management
174
        //
175
        if (!is_null($theme))
176
        {
177
            $logger->notice("Looking for '${theme}' theme...");
178
179
            $this->tm = new ThemeManager($theme);
180
            $this->tm->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
181
            $this->tm->setLogger($this->output);
182
            $this->tm->setFolder($this->outputDirectory);
183
            $this->tm->copyFiles();
184
        }
185
186
        //
187
        // Static file management
188
        //
189
        $this->am = new AssetManager();
190
        $this->am->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
191
        $this->am->setLogger($this->output);
192
        $this->am->setFolder($this->outputDirectory);
193
        $this->am->copyFiles();
194
    }
195
196
    public function watch()
197
    {
198
        $this->output->writeln('Building website...');
199
        $this->build(true);
200
        $this->output->writeln(sprintf('Watching %s', getcwd()));
201
202
        $exclusions = array_merge($this->getConfiguration()->getExcludes(), array(
203
            $this->getConfiguration()->getTargetFolder()
204
        ));
205
        $fileExplorer = FileExplorer::create(
206
            getcwd(), $exclusions, $this->getConfiguration()->getIncludes()
207
        );
208
209
        $newWatcher = Watcher::create(getcwd());
210
        $newWatcher
211
            ->setLogger($this->output)
212
            ->setEventDispatcher($this->container->get('event_dispatcher'))
213
            ->setExcludePatterns(array_merge(
214
                $exclusions, FileExplorer::$vcsPatterns, array(Configuration::CACHE_FOLDER)
215
            ))
216
            ->setIterator($fileExplorer->getExplorer())
217
//            ->addListener(Create::NAME, function ($e) { $this->watchListenerFunction($e); })
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
218
//            ->addListener(Modify::NAME, function ($e) { $this->watchListenerFunction($e); })
219
//            ->addListener(Move::NAME,   function ($e) { $this->watchListenerFunction($e); })
220
        ;
221
222
        $this->output->writeln('Watch started successfully');
223
224
        $newWatcher->start();
225
    }
226
227
    private function watchListenerFunction(AbstractEvent $event)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
228
    {
229
        $filePath = $this->fs->getRelativePath($event->filename);
230
231
        try
232
        {
233
            switch ($event::getEventName())
234
            {
235
                case Create::NAME:
236
                    $this->creationWatcher($filePath);
237
                    break;
238
239
                case Modify::NAME:
240
                    $this->modificationWatcher($filePath);
241
                    break;
242
243
                case Move::NAME:
244
                    $newFile = $this->fs->getRelativePath($event->destFilename);
0 ignored issues
show
Bug introduced by
The property destFilename does not seem to exist. Did you mean filename?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
245
246
                    $this->deletionWatcher($filePath);
0 ignored issues
show
Unused Code introduced by
The call to the method allejo\stakx\Website::deletionWatcher() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
247
                    $this->creationWatcher($newFile);
248
                    break;
249
            }
250
        }
251
        catch (FileAwareException $e)
252
        {
253
            $this->output->writeln(sprintf("Your website failed to build with the following error in file '%s': %s",
254
                $e->getPath(),
255
                $e->getMessage()
256
            ));
257
        }
258
        catch (\Exception $e)
259
        {
260
            $this->output->writeln(sprintf('Your website failed to build with the following error: %s',
261
                $e->getMessage()
262
            ));
263
        }
264
    }
265
266
    /**
267
     * @return Configuration
268
     */
269
    public function getConfiguration()
270
    {
271
        return $this->container->get(Configuration::class);
272
    }
273
274
    /**
275
     * Get whether or not the website is being built in Configuration-less mode.
276
     *
277
     * @return bool True when being built with no configuration file
278
     */
279
    public function isConfLess()
280
    {
281
        return $this->confLess;
282
    }
283
284
    /**
285
     * Set whether or not the website should be built with a configuration.
286
     *
287
     * @param bool $status True when a website should be built without a configuration
288
     */
289
    public function setConfLess($status)
290
    {
291
        $this->confLess = $status;
292
    }
293
294
    /**
295
     * @param string $filePath
296
     */
297
    private function creationWatcher($filePath, $newlyCreate = true)
298
    {
299
        if ($newlyCreate)
300
        {
301
            $this->output->writeln(sprintf('File creation detected: %s', $filePath));
302
        }
303
304
        if ($this->pm->shouldBeTracked($filePath))
305
        {
306
            try
307
            {
308
                $pageView = $this->pm->createNewItem($filePath);
309
310
                $this->compiler->compilePageView($pageView);
311
312
                unset($this->creationQueue[$filePath]);
313
            }
314
            catch (\Exception $e)
315
            {
316
                $this->creationQueue[$filePath] = true;
317
            }
318
        }
319
        elseif ($this->cm->shouldBeTracked($filePath))
320
        {
321
            try
322
            {
323
                $contentItem = $this->cm->createNewItem($filePath);
324
                TwigManager::getInstance()->addGlobal('collections', $this->cm->getCollections());
325
326
                $this->pm->trackNewContentItem($contentItem);
0 ignored issues
show
Compatibility introduced by
$contentItem of type object<allejo\stakx\Document\ReadableDocument> is not a sub-type of object<allejo\stakx\Document\ContentItem>. It seems like you assume a child class of the class allejo\stakx\Document\ReadableDocument to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
327
                $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...
328
                $this->compiler->compileSome(array(
329
                    'namespace'  => 'collections',
330
                    'dependency' => $contentItem->getNamespace(),
331
                ));
332
333
                unset($this->creationQueue[$filePath]);
334
            }
335
            catch (\Exception $e)
336
            {
337
                $this->creationQueue[$filePath] = true;
338
            }
339
        }
340 View Code Duplication
        elseif ($this->dm->shouldBeTracked($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...
341
        {
342
            $change = $this->dm->createNewItem($filePath);
343
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
344
345
            $this->compiler->compileSome(array(
346
                'namespace'  => 'data',
347
                'dependency' => $change,
348
            ));
349
        }
350
        elseif (!is_null($this->tm) && $this->tm->shouldBeTracked($filePath))
351
        {
352
            $this->tm->createNewItem($filePath);
353
        }
354
        elseif ($this->am->shouldBeTracked($filePath))
355
        {
356
            $this->am->createNewItem($filePath);
357
        }
358
    }
359
360
    /**
361
     * @param string $filePath
362
     */
363
    private function modificationWatcher($filePath)
364
    {
365
        $this->container->get('logger')->writeln(sprintf('File change detected: %s', $filePath));
366
367
        if (isset($this->creationQueue[$filePath]))
368
        {
369
            $this->creationWatcher($filePath, false);
370
        }
371
        elseif ($this->compiler->isParentTemplate($filePath))
372
        {
373
            TwigManager::getInstance()->clearTemplateCache();
0 ignored issues
show
Deprecated Code introduced by
The method Twig_Environment::clearTemplateCache() has been deprecated with message: since 1.18.3 (to be removed in 2.0)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
374
            $this->compiler->refreshParent($filePath);
375
        }
376
        elseif ($this->compiler->isImportDependency($filePath))
377
        {
378
            TwigManager::getInstance()->clearTemplateCache();
0 ignored issues
show
Deprecated Code introduced by
The method Twig_Environment::clearTemplateCache() has been deprecated with message: since 1.18.3 (to be removed in 2.0)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
379
            $this->compiler->compileImportDependencies($filePath);
380
        }
381
        elseif ($this->pm->isTracked($filePath))
382
        {
383
            $change = $this->pm->refreshItem($filePath);
384
385
            TwigManager::getInstance()->clearTemplateCache();
0 ignored issues
show
Deprecated Code introduced by
The method Twig_Environment::clearTemplateCache() has been deprecated with message: since 1.18.3 (to be removed in 2.0)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
386
            $this->compiler->compilePageView($change);
387
        }
388
        elseif ($this->cm->isTracked($filePath))
389
        {
390
            $contentItem = &$this->cm->getContentItem($filePath);
391
            $contentItem->readContent();
392
393
            $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...
394
            $this->compiler->compileSome(array(
395
                'namespace'  => 'collections',
396
                'dependency' => $contentItem->getNamespace(),
397
            ));
398
        }
399 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...
400
        {
401
            $change = $this->dm->refreshItem($filePath);
402
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
403
404
            $this->compiler->compileSome(array(
405
                'namespace'  => 'data',
406
                'dependency' => $change,
407
            ));
408
        }
409
        elseif (!is_null($this->tm) && $this->tm->isTracked($filePath))
410
        {
411
            $this->tm->refreshItem($filePath);
412
        }
413
        elseif ($this->am->isTracked($filePath))
414
        {
415
            $this->am->refreshItem($filePath);
416
        }
417
    }
418
419
    /**
420
     * @param string $filePath
421
     */
422
    private function deletionWatcher($filePath)
0 ignored issues
show
Unused Code introduced by
The parameter $filePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
423
    {
424
    }
425
426
    /**
427
     * Prepare the Stakx environment by creating necessary cache folders.
428
     */
429
    private function createFolderStructure()
430
    {
431
        $targetDir = $this->fs->absolutePath($this->getConfiguration()->getTargetFolder());
432
433
        if (!Service::getParameter(BuildableCommand::NO_CLEAN))
434
        {
435
            $this->fs->remove($targetDir);
436
        }
437
438
        if (!Service::getParameter(BuildableCommand::USE_CACHE))
439
        {
440
            $this->fs->remove($this->fs->absolutePath(Configuration::CACHE_FOLDER, 'twig'));
441
            $this->fs->mkdir($this->fs->absolutePath($this->fs->appendPath(Configuration::CACHE_FOLDER, 'twig')));
442
        }
443
444
        $this->fs->mkdir($targetDir);
445
    }
446
447
    /**
448
     * Configure the Highlighter object for highlighting code blocks.
449
     */
450
    private function configureHighlighter()
451
    {
452
        $enabled = Service::setParameter(Configuration::HIGHLIGHTER_ENABLED, $this->getConfiguration()->isHighlighterEnabled());
453
454
        if (!$enabled)
455
        {
456
          return;
457
        }
458
459
        foreach ($this->getConfiguration()->getHighlighterCustomLanguages() as $lang => $path)
460
        {
461
            $fullPath = $this->fs->absolutePath($path);
462
463
            if (!$this->fs->exists($fullPath))
464
            {
465
                $this->output->warning('The following language definition could not be found: {lang}', array(
466
                    'lang' => $path
467
                ));
468
                continue;
469
            }
470
471
            Highlighter::registerLanguage($lang, $fullPath);
472
            $this->output->debug('Loading custom language {lang} from {path}...', array(
473
                'lang' => $lang,
474
                'path' => $path
475
            ));
476
        }
477
    }
478
}
479