Completed
Push — master ( 2eed8d...2bc4b2 )
by Vladimir
02:25
created

Website::setConfLess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 4
rs 10
ccs 0
cts 3
cp 0
crap 2
1
<?php
2
3
namespace allejo\stakx\Object;
4
5
use allejo\stakx\Core\ConsoleInterface;
6
use allejo\stakx\Engines\TwigMarkdownEngine;
7
use allejo\stakx\Manager\PageManager;
8
use allejo\stakx\System\Filesystem;
9
use allejo\stakx\Manager\CollectionManager;
10
use allejo\stakx\Manager\DataManager;
11
use allejo\stakx\System\Folder;
12
use allejo\stakx\Twig\FilesystemExtension;
13
use allejo\stakx\Twig\TwigExtension;
14
use Aptoma\Twig\Extension\MarkdownExtension;
15
use JasonLewis\ResourceWatcher\Tracker;
16
use JasonLewis\ResourceWatcher\Watcher;
17
use Symfony\Component\Console\Output\OutputInterface;
18
use Symfony\Component\Finder\SplFileInfo;
19
use Symfony\Component\Yaml\Yaml;
20
use Twig_Environment;
21
use Twig_Loader_Filesystem;
22
23
class Website
24
{
25
    private static $twig_ref;
26
27
    /**
28
     * The Twig environment that will be used to render pages. This includes all of the loaded extensions and global
29
     * variables.
30
     *
31
     * @var Twig_Environment
32
     */
33
    private $twig;
34
35
    /**
36
     * The location of where the compiled website will be written to
37
     *
38
     * @var Folder
39
     */
40
    private $outputDirectory;
41
42
    /**
43
     * The main configuration to be used to build the specified website
44
     *
45
     * @var Configuration
46
     */
47
    private $configuration;
48
49
    /**
50
     * When set to true, the Stakx website will be built without a configuration file
51
     *
52
     * @var bool
53
     */
54
    private $confLess;
55
56
    /**
57
     * When set to true, Twig templates will not have access to filters or functions which provide access to the
58
     * filesystem
59
     *
60
     * @var bool
61
     */
62
    private $safeMode;
63
64
    /**
65
     * @var ConsoleInterface
66
     */
67
    private $output;
68
69
    /**
70
     * @var CollectionManager
71
     */
72
    private $cm;
73
74
    /**
75
     * @var DataManager
76
     */
77
    private $dm;
78
79
    /**
80
     * @var Filesystem
81
     */
82
    private $fs;
83
84
    /**
85
     * @var PageManager
86
     */
87
    private $pm;
88
89
    /**
90
     * Website constructor.
91
     *
92
     * @param OutputInterface $output
93
     */
94
    public function __construct (OutputInterface $output)
95
    {
96
        $this->output = new ConsoleInterface($output);
97
        $this->cm = new CollectionManager();
98
        $this->dm = new DataManager();
99
        $this->pm = new PageManager();
100
        $this->fs = new Filesystem();
101
    }
102
103
    /**
104
     * Compile the website.
105
     *
106
     * @param bool $cleanDirectory Clean the target directing before rebuilding
107
     */
108
    public function build ($cleanDirectory)
109
    {
110
        // Parse DataItems
111
        $this->dm->setConsoleOutput($this->output);
112
        $this->dm->parseDataItems($this->getConfiguration()->getDataFolders());
0 ignored issues
show
Documentation introduced by
$this->getConfiguration()->getDataFolders() is of type integer|string|null, but the function expects a array<integer,string>.

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...
113
        $this->dm->parseDataSets($this->getConfiguration()->getDataSets());
0 ignored issues
show
Documentation introduced by
$this->getConfiguration()->getDataSets() is of type integer|string|null, but the function expects a array<integer,string>.

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...
114
115
        // Prepare Collections
116
        $this->cm->setConsoleOutput($this->output);
117
        $this->cm->parseCollections($this->getConfiguration()->getCollectionsFolders());
118
119
        // Handle PageViews
120
        $this->pm->setConsoleOutput($this->output);
121
        $this->pm->setTwig($this->twig);
122
        $this->pm->parsePageViews($this->getConfiguration()->getPageViewFolders());
123
        $this->pm->prepareDynamicPageViews($this->cm->getCollections());
0 ignored issues
show
Documentation introduced by
$this->cm->getCollections() is of type array<integer,array<inte...x\Object\ContentItem>>>, but the function expects a array<integer,object<all...kx\Object\ContentItem>>.

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...
124
125
        // Configure the environment
126
        $this->createFolderStructure($cleanDirectory);
127
        $this->configureTwig();
128
129
        // Our output directory
130
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
131
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
132
133
        // Copy over assets
134
        $this->output->notice('Copying theme assets...');
135
        $this->copyThemeAssets();
136
137
        $this->output->notice('Copying static files...');
138
        $this->copyStaticFiles();
139
140
        // Compile everything
141
        $this->output->notice('Compiling files...');
142
        $this->pm->compileAll(
143
            $this->outputDirectory
144
        );
145
    }
146
147
    public function watch ()
148
    {
149
        $this->build(true);
150
151
        $tracker    = new Tracker();
152
        $watcher    = new Watcher($tracker, $this->fs);
153
        $listener   = $watcher->watch(getcwd());
154
        $targetPath = $this->getConfiguration()->getTargetFolder();
155
156
        $this->output->notice('Watch started successfully');
157
158
        $listener->onModify(function ($resource, $path) use ($targetPath) {
159
            $filePath = $this->fs->getRelativePath($path);
160
161
            if ((substr($filePath, 0, strlen($targetPath)) === $targetPath) ||
162
                (substr($filePath, 0, 1) === '.'))
163
            {
164
                return;
165
            }
166
167
            $this->output->writeln(sprintf("File change detected: %s", $filePath));
168
169
            try
170
            {
171
                $pageViewCompiled = $this->pm->compileSingle($filePath);
0 ignored issues
show
Unused Code introduced by
$pageViewCompiled 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...
172
            }
173
            catch (\Exception $e)
174
            {
175
                $this->output->error(sprintf("Your website failed to build with the following error: %s",
176
                    $e->getMessage()
177
                ));
178
            }
179
        });
180
181
        $watcher->start();
182
    }
183
184
    /**
185
     * @return Configuration
186
     */
187
    public function getConfiguration ()
188
    {
189
        return $this->configuration;
190
    }
191
192
    /**
193
     * @param string $configFile
194
     *
195
     * @throws \LogicException
196
     */
197
    public function setConfiguration ($configFile)
198
    {
199
        if (!$this->fs->exists($configFile) && !$this->isConfLess())
200
        {
201
            $this->output->error("You are trying to build a website in a directory without a configuration file. Is this what you meant to do?");
202
            $this->output->error("To build a website without a configuration, use the '--no-conf' option");
203
204
            throw new \LogicException("Cannot build a website without a configuration when not in Configuration-less mode");
205
        }
206
207
        if ($this->isConfLess())
208
        {
209
            $configFile = "";
210
        }
211
212
        $this->configuration = new Configuration($configFile, $this->output);
213
    }
214
215
    /**
216
     * Get whether or not the website is being built in Configuration-less mode
217
     *
218
     * @return bool True when being built with no configuration file
219
     */
220
    public function isConfLess ()
221
    {
222
        return $this->confLess;
223
    }
224
225
    /**
226
     * Set whether or not the website should be built with a configuration
227
     *
228
     * @param bool $status True when a website should be built without a configuration
229
     */
230
    public function setConfLess ($status)
231
    {
232
        $this->confLess = $status;
233
    }
234
235
    /**
236
     * Get whether or not the website is being built in safe mode.
237
     *
238
     * Safe mode is defined as disabling file system access from Twig and disabling user Twig extensions
239
     *
240
     * @return bool True when the website is being built in safe mode
241
     */
242
    public function isSafeMode ()
243
    {
244
        return $this->safeMode;
245
    }
246
247
    /**
248
     * Set whether a website should be built in safe mode
249
     *
250
     * @param bool $bool True if a website should be built in safe mode
251
     */
252
    public function setSafeMode ($bool)
253
    {
254
        $this->safeMode = $bool;
255
    }
256
257 3
    public static function getTwigInstance ()
258
    {
259 3
        return self::$twig_ref;
260
    }
261
262
    /**
263
     * Prepare the Stakx environment by creating necessary cache folders
264
     *
265
     * @param bool $cleanDirectory Clean the target directory
266
     */
267
    private function createFolderStructure ($cleanDirectory)
268
    {
269
        $tarDir = $this->fs->absolutePath($this->configuration->getTargetFolder());
270
271
        if ($cleanDirectory)
272
        {
273
            $this->fs->remove($tarDir);
274
        }
275
276
        $this->fs->remove($this->fs->absolutePath('.stakx-cache'));
277
        $this->fs->mkdir('.stakx-cache/twig');
278
        $this->fs->mkdir($tarDir);
279
    }
280
281
    /**
282
     * Configure the Twig environment used by Stakx. This includes loading themes, global variables, extensions, and
283
     * debug settings.
284
     *
285
     * @todo Load custom Twig extensions from _config.yml
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
286
     */
287
    private function configureTwig ()
288
    {
289
        $loader   = new Twig_Loader_Filesystem(array(
290
            getcwd()
291
        ));
292
        $theme    = $this->configuration->getTheme();
293
        $mdEngine = new TwigMarkdownEngine();
294
295
        // Only load a theme if one is specified and actually exists
296
        if (!is_null($theme))
297
        {
298
            try
299
            {
300
                $loader->addPath($this->fs->absolutePath('_themes', $this->configuration->getTheme()), 'theme');
301
            }
302
            catch (\Twig_Error_Loader $e)
303
            {
304
                $this->output->error("The following theme could not be loaded: {theme}", array(
305
                    "theme" => $theme
306
                ));
307
                $this->output->error($e->getMessage());
308
            }
309
        }
310
311
        $this->twig = new Twig_Environment($loader, array(
312
            'autoescape' => $this->getConfiguration()->getTwigAutoescape(),
313
            //'cache'      => '.stakx-cache/twig'
314
        ));
315
316
        $this->twig->addGlobal('site', $this->configuration->getConfiguration());
317
        $this->twig->addGlobal('collections', $this->cm->getCollections());
318
        $this->twig->addGlobal('menu', $this->pm->getSiteMenu());
319
        $this->twig->addGlobal('data', $this->dm->getDataItems());
320
        $this->twig->addExtension(new TwigExtension());
321
        $this->twig->addExtension(new \Twig_Extensions_Extension_Text());
322
        $this->twig->addExtension(new MarkdownExtension($mdEngine));
323
324
        if (!$this->safeMode)
325
        {
326
            $this->twig->addExtension(new FilesystemExtension());
327
        }
328
329
        if ($this->configuration->isDebug())
330
        {
331
            $this->twig->addExtension(new \Twig_Extension_Debug());
332
            $this->twig->enableDebug();
333
        }
334
335
        self::$twig_ref = &$this->twig;
336
    }
337
338
    /**
339
     * Copy static files from a theme to the compiled website
340
     */
341
    private function copyThemeAssets ()
342
    {
343
        $theme = $this->configuration->getTheme();
344
345
        if (is_null($theme))
346
        {
347
            return;
348
        }
349
350
        $themeFolder = $this->fs->appendPath("_themes", $theme);
351
        $themeFile   = $this->fs->absolutePath($themeFolder, "stakx-theme.yml");
352
        $themeData   = array();
353
354
        if ($this->fs->exists($themeFile))
355
        {
356
            $themeData = Yaml::parse(file_get_contents($themeFile));
357
        }
358
359
        foreach ($themeData['include'] as &$include)
360
        {
361
            $include = $this->fs->appendPath($themeFolder, $include);
362
        }
363
364
        $finder = $this->fs->getFinder(
365
            array_merge(
366
                $this->getConfiguration()->getIncludes(),
367
                $themeData['include']
368
            ),
369
            array_merge(
370
                $this->getConfiguration()->getExcludes(),
371
                $themeData['exclude'],
372
                array('.twig')
373
            ),
374
            $this->fs->absolutePath($themeFolder)
375
        );
376
377
        /** @var SplFileInfo $file */
378
        foreach ($finder as $file)
379
        {
380
            $this->copyToCompiledSite($file, $themeFolder);
381
        }
382
    }
383
384
    /**
385
     * Copy the static files from the current directory into the compiled website directory.
386
     *
387
     * Static files are defined as follows:
388
     *   - Does not start with an underscore or is inside of a directory starting with an underscore
389
     *   - Does not start with a period or is inside of a directory starting with a period
390
     */
391
    private function copyStaticFiles ()
392
    {
393
        $finder = $this->fs->getFinder(
394
            $this->getConfiguration()->getIncludes(),
0 ignored issues
show
Documentation introduced by
$this->getConfiguration()->getIncludes() is of type integer|string|null, but the function expects a array.

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...
395
            $this->getConfiguration()->getExcludes()
0 ignored issues
show
Documentation introduced by
$this->getConfiguration()->getExcludes() is of type integer|string|null, but the function expects a array.

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...
396
        );
397
398
        /** @var $file SplFileInfo */
399
        foreach ($finder as $file)
400
        {
401
            $this->copyToCompiledSite($file);
402
        }
403
    }
404
405
    /**
406
     * Copy a file from a the source directory to the compiled website directory. The exact relative path to the file
407
     * will be recreated in the compiled website directory.
408
     *
409
     * @param SplFileInfo $file   The relative path of the file to be copied
410
     * @param string      $prefix
411
     */
412
    private function copyToCompiledSite ($file, $prefix = "")
413
    {
414
        if (!$this->fs->exists($file)) { return; }
415
416
        $filePath = $file->getRealPath();
417
        $pathToStrip = $this->fs->appendPath(getcwd(), $prefix);
418
        $siteTargetPath = ltrim(str_replace($pathToStrip, "", $filePath), DIRECTORY_SEPARATOR);
419
420
        try
421
        {
422
            $this->outputDirectory->copyFile($filePath, $siteTargetPath);
423
        }
424
        catch (\Exception $e)
425
        {
426
            $this->output->error($e->getMessage());
427
        }
428
    }
429
}