Passed
Push — master ( 451245...2b5fd7 )
by Brent
03:56
created

SiteParser::createMeta()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Brendt\Stitcher;
4
5
use Brendt\Html\Meta\Meta;
6
use Brendt\Stitcher\Event\Event;
7
use Brendt\Stitcher\Exception\TemplateNotFoundException;
8
use Brendt\Stitcher\Factory\AdapterFactory;
9
use Brendt\Stitcher\Factory\HeaderCompilerFactory;
10
use Brendt\Stitcher\Factory\ParserFactory;
11
use Brendt\Stitcher\Factory\TemplateEngineFactory;
12
use Brendt\Stitcher\Site\Http\HeaderCompiler;
13
use Brendt\Stitcher\Site\Meta\MetaCompiler;
14
use Brendt\Stitcher\Site\Page;
15
use Brendt\Stitcher\Site\Site;
16
use Brendt\Stitcher\Template\TemplateEngine;
17
use Brendt\Stitcher\Template\TemplatePlugin;
18
use Symfony\Component\EventDispatcher\EventDispatcher;
19
use Symfony\Component\Finder\Finder;
20
use Symfony\Component\Finder\SplFileInfo;
21
use Symfony\Component\Yaml\Exception\ParseException;
22
use Symfony\Component\Yaml\Yaml;
23
24
class SiteParser
25
{
26
    const EVENT_PARSER_INIT = 'parser.initialised';
27
28
    const EVENT_PAGE_PARSING = 'page.parsing';
29
30
    const EVENT_PAGE_PARSED = 'page.parsed';
31
32
    /**
33
     * @var string
34
     */
35
    private $filter;
36
37
    /**
38
     * @var string
39
     */
40
    private $srcDir;
41
42
    /**
43
     * @var string
44
     */
45
    private $templateDir;
46
47
    /**
48
     * @var ParserFactory
49
     */
50
    private $parserFactory;
51
52
    /**
53
     * @var TemplateEngineFactory
54
     */
55
    private $templateEngineFactory;
56
57
    /**
58
     * @var AdapterFactory
59
     */
60
    private $adapterFactory;
61
62
    /**
63
     * @var HeaderCompilerFactory
64
     */
65
    private $headerCompilerFactory;
66
67
    /**
68
     * @var array
69
     */
70
    private $metaConfig;
71
72
    /**
73
     * @var TemplatePlugin
74
     */
75
    private $templatePlugin;
76
77
    /**
78
     * @var HeaderCompiler|null
79
     */
80
    private $headerCompiler;
81
82
    /**
83
     * @var TemplateEngine
84
     */
85
    private $templateEngine;
86
87
    /**
88
     * @var SplFileInfo[]
89
     */
90
    private $templates;
91
92
    /**
93
     * @var MetaCompiler
94
     */
95
    private $metaCompiler;
96
97
    /**
98
     * @var EventDispatcher
99
     */
100
    private $eventDispatcher;
101
102
    /**
103
     * SiteParser constructor.
104
     *
105
     * @param string                $srcDir
106
     * @param string                $templateDir
107
     * @param TemplatePlugin        $templatePlugin
108
     * @param ParserFactory         $parserFactory
109
     * @param TemplateEngineFactory $templateEngineFactory
110
     * @param AdapterFactory        $adapterFactory
111
     * @param HeaderCompilerFactory $headerCompilerFactory
112
     * @param MetaCompiler          $metaCompiler
113
     * @param EventDispatcher       $eventDispatcher
114
     * @param array                 $metaConfig
115
     */
116
    public function __construct(
117
        string $srcDir,
118
        string $templateDir,
119
        TemplatePlugin $templatePlugin,
120
        ParserFactory $parserFactory,
121
        TemplateEngineFactory $templateEngineFactory,
122
        AdapterFactory $adapterFactory,
123
        HeaderCompilerFactory $headerCompilerFactory,
124
        MetaCompiler $metaCompiler,
125
        EventDispatcher $eventDispatcher,
126
        array $metaConfig = []
127
    ) {
128
        $this->srcDir = $srcDir;
129
        $this->templateDir = $templateDir;
130
        $this->templatePlugin = $templatePlugin;
131
        $this->parserFactory = $parserFactory;
132
        $this->templateEngineFactory = $templateEngineFactory;
133
        $this->adapterFactory = $adapterFactory;
134
        $this->headerCompilerFactory = $headerCompilerFactory;
135
        $this->metaCompiler = $metaCompiler;
136
        $this->eventDispatcher = $eventDispatcher;
137
        $this->metaConfig = $metaConfig;
138
139
        $this->headerCompiler = $this->headerCompilerFactory->getHeaderCompilerByEnvironment();
140
        $this->templateEngine = $this->templateEngineFactory->getDefault();
141
        $this->templates = $this->loadTemplates();
142
    }
143
144
    /**
145
     * Load a site from YAML configuration files in the `directories.src`/site directory.
146
     * All YAML files are loaded and parsed into Page objects and added to a Site collection.
147
     *
148
     * @param array $routes
149
     *
150
     * @return Site
151
     * @throws InvalidSiteException
152
     * @see \Brendt\Stitcher\Site\Page
153
     * @see \Brendt\Stitcher\Site\Site
154
     */
155
    public function loadSite(array $routes = []) : Site {
156
        /** @var SplFileInfo[] $files */
157
        $files = Finder::create()->files()->in("{$this->srcDir}/site")->name('*.yml');
158
        $site = new Site();
159
160
        foreach ($files as $file) {
161
            try {
162
                $fileContents = (array) Yaml::parse($file->getContents());
163
            } catch (ParseException $e) {
164
                throw new InvalidSiteException("{$file->getRelativePathname()}: {$e->getMessage()}");
165
            }
166
167
            foreach ($fileContents as $route => $data) {
168
                if (count($routes) && !in_array($route, $routes)) {
169
                    continue;
170
                }
171
172
                $page = new Page($route, $data, $this->createMeta());
173
                $site->addPage($page);
174
            }
175
        }
176
177
        return $site;
178
    }
179
180
    /**
181
     * Load all templates from either the `directories.template` directory. Depending on the configured template
182
     * engine, set with `engines.template`; .html or .tpl files will be loaded.
183
     *
184
     * @return SplFileInfo[]
185
     */
186
    public function loadTemplates() {
187
        $templateExtension = $this->templateEngine->getTemplateExtension();
188
189
        /** @var SplFileInfo[] $files */
190
        $files = Finder::create()->files()->in($this->templateDir)->name("*.{$templateExtension}");
191
        $templates = [];
192
193
        foreach ($files as $file) {
194
            $id = str_replace(".{$templateExtension}", '', $file->getRelativePathname());
195
            $templates[$id] = $file;
196
        }
197
        
198
        return $templates;
199
    }
200
201
    /**
202
     * Parse a path into usable data.
203
     *
204
     * @param array  $routes
205
     * @param string $filterValue
206
     *
207
     * @return array|mixed
208
     * @throws TemplateNotFoundException
209
     */
210
    public function parse($routes = [], string $filterValue = null) : array {
211
        $blanket = [];
212
213
        $site = $this->loadSite((array) $routes);
214
        $this->eventDispatcher->dispatch(self::EVENT_PARSER_INIT, Event::create(['site' => $site]));
215
216
        foreach ($site as $page) {
217
            $this->eventDispatcher->dispatch(self::EVENT_PAGE_PARSING, Event::create(['page' => $page]));
218
219
            $templateIsset = isset($this->templates[$page->getTemplatePath()]);
220
221
            if (!$templateIsset) {
222
                if ($template = $page->getTemplatePath()) {
223
                    throw new TemplateNotFoundException("Template {$template} not found.");
224
                } else {
225
                    throw new TemplateNotFoundException('No template was set.');
226
                }
227
            }
228
229
            $pages = $this->parseAdapters($page, $filterValue);
230
231
            /** @var Page $entryPage */
232
            foreach ($pages as $entryPage) {
233
                $blanket[$entryPage->getId()] = $this->parsePage($entryPage);
234
            }
235
236
            $this->eventDispatcher->dispatch(self::EVENT_PAGE_PARSED, Event::create(['page' => $page]));
237
        }
238
239
        return $blanket;
240
    }
241
242
    /**
243
     * @param Page $page
244
     *
245
     * @return string
246
     */
247
    public function parsePage(Page $page) : string {
248
        $entryPage = $this->parseVariables($page);
249
        $this->metaCompiler->compilePage($page);
250
251
        $this->templatePlugin->setPage($entryPage);
252
        $this->templateEngine->addTemplateVariables($entryPage->getVariables());
253
254
        $pageTemplate = $this->templates[$page->getTemplatePath()];
255
        $result = $this->templateEngine->renderTemplate($pageTemplate);
256
        
257
        if ($this->headerCompiler) {
258
            $this->headerCompiler->compilePage($page);
259
        }
260
261
        $this->templateEngine->clearTemplateVariables();
262
263
        return $result;
264
    }
265
266
    /**
267
     * This function takes a page and optional entry id. The page's adapters will be loaded and looped.
268
     * An adapter will transform a page's original configuration and variables to one or more pages.
269
     * An entry id can be provided as a filter. This filter can be used in an adapter to skip rendering unnecessary
270
     * pages. The filter parameter is used to render pages on the fly when using the developer controller.
271
     *
272
     * @param Page   $page
273
     * @param string $entryId
274
     *
275
     * @return Page[]
276
     *
277
     * @see  \Brendt\Stitcher\Adapter\Adapter::transform()
278
     * @see  \Brendt\Stitcher\Application\DevController::run()
279
     */
280
    public function parseAdapters(Page $page, $entryId = null) {
281
        if (!$page->getAdapters()) {
282
            return [$page->getId() => $page];
283
        }
284
285
        $pages = [$page];
286
287
        foreach ($page->getAdapters() as $type => $adapterConfig) {
288
            $adapter = $this->adapterFactory->getByType($type);
289
290
            if ($entryId !== null) {
291
                $pages = $adapter->transform($pages, $entryId);
292
            } else {
293
                $pages = $adapter->transform($pages);
294
            }
295
        }
296
297
        return $pages;
298
    }
299
300
    /**
301
     * This function takes a Page object and parse its variables using a Parser. It will only parse variables which
302
     * weren't parsed already by an adapter.
303
     *
304
     * @param Page $page
305
     *
306
     * @return Page
307
     *
308
     * @see \Brendt\Stitcher\Factory\ParserFactory
309
     * @see \Brendt\Stitcher\Parser\Parser
310
     * @see \Brendt\Stitcher\Site\Page::isParsedVariable()
311
     */
312
    public function parseVariables(Page $page) {
313
        foreach ($page->getVariables() as $name => $value) {
314
            if ($page->isParsedVariable($name)) {
315
                continue;
316
            }
317
318
            $page
319
                ->setVariableValue($name, $this->getData($value))
320
                ->setVariableIsParsed($name);
321
        }
322
323
        return $page;
324
    }
325
326
    /**
327
     * @param string $filter
328
     *
329
     * @return SiteParser
330
     */
331
    public function setFilter(string $filter) : SiteParser {
332
        $this->filter = $filter;
333
334
        return $this;
335
    }
336
337
    /**
338
     * This function will get the parser based on the value. This value is parsed by the parser, or returned if no
339
     * suitable parser was found.
340
     *
341
     * @param $value
342
     *
343
     * @return mixed
344
     *
345
     * @see \Brendt\Stitcher\Factory\ParserFactory
346
     */
347
    private function getData($value) {
348
        $parser = $this->parserFactory->getByFileName($value);
349
350
        if (!$parser) {
351
            return $value;
352
        }
353
354
        return $parser->parse($value);
355
    }
356
357
    private function createMeta() : Meta {
358
        $meta = new Meta();
359
360
        return $meta;
361
    }
362
}
363