1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Brendt\Stitcher\Parser\Site; |
4
|
|
|
|
5
|
|
|
use Brendt\Stitcher\Site\Seo\SiteMap; |
6
|
|
|
use Pageon\Html\Meta\Meta; |
7
|
|
|
use Brendt\Stitcher\Event\Event; |
8
|
|
|
use Brendt\Stitcher\Exception\InvalidSiteException; |
9
|
|
|
use Brendt\Stitcher\Exception\TemplateNotFoundException; |
10
|
|
|
use Brendt\Stitcher\Site\Http\Htaccess; |
11
|
|
|
use Brendt\Stitcher\Site\Page; |
12
|
|
|
use Brendt\Stitcher\Site\Site; |
13
|
|
|
use Pageon\Pcntl\Manager; |
14
|
|
|
use Pageon\Pcntl\PageRenderProcess; |
15
|
|
|
use Pageon\Pcntl\ProcessCollection; |
16
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcher; |
17
|
|
|
use Symfony\Component\Finder\Finder; |
18
|
|
|
use Symfony\Component\Finder\SplFileInfo; |
19
|
|
|
use Symfony\Component\Yaml\Exception\ParseException; |
20
|
|
|
use Symfony\Component\Yaml\Yaml; |
21
|
|
|
|
22
|
|
|
class SiteParser |
23
|
|
|
{ |
24
|
|
|
const EVENT_PARSER_INIT = 'parser.initialised'; |
25
|
|
|
|
26
|
|
|
const EVENT_PAGE_PARSING = 'page.parsing'; |
27
|
|
|
|
28
|
|
|
const EVENT_PAGE_PARSED = 'page.parsed'; |
29
|
|
|
|
30
|
|
|
const TOKEN_REDIRECT = 'redirect'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
private $filter; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
private $srcDir; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array |
44
|
|
|
*/ |
45
|
|
|
private $metaConfig; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var EventDispatcher |
49
|
|
|
*/ |
50
|
|
|
private $eventDispatcher; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var PageParser |
54
|
|
|
*/ |
55
|
|
|
private $pageParser; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var Htaccess |
59
|
|
|
*/ |
60
|
|
|
private $htaccess; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var string |
64
|
|
|
*/ |
65
|
|
|
private $publicDir; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var bool |
69
|
|
|
*/ |
70
|
|
|
private $async; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @var string |
74
|
|
|
*/ |
75
|
|
|
private $environment; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var SiteMap |
79
|
|
|
*/ |
80
|
|
|
private $siteMap; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* SiteParser constructor. |
84
|
|
|
* |
85
|
|
|
* @param string $srcDir |
86
|
|
|
* @param string $publicDir |
87
|
|
|
* @param string $environment |
88
|
|
|
* @param bool $async |
89
|
|
|
* @param EventDispatcher $eventDispatcher |
90
|
|
|
* @param PageParser $pageParser |
91
|
|
|
* @param Htaccess $htaccess |
92
|
|
|
* @param SiteMap $siteMap |
93
|
|
|
* @param array $metaConfig |
94
|
|
|
*/ |
95
|
|
View Code Duplication |
public function __construct( |
|
|
|
|
96
|
|
|
string $srcDir, |
97
|
|
|
string $publicDir, |
98
|
|
|
string $environment, |
99
|
|
|
bool $async, |
100
|
|
|
EventDispatcher $eventDispatcher, |
101
|
|
|
PageParser $pageParser, |
102
|
|
|
Htaccess $htaccess, |
103
|
|
|
SiteMap $siteMap, |
104
|
|
|
array $metaConfig = [] |
105
|
|
|
) { |
106
|
|
|
$this->srcDir = $srcDir; |
107
|
|
|
$this->publicDir = $publicDir; |
108
|
|
|
$this->eventDispatcher = $eventDispatcher; |
109
|
|
|
$this->pageParser = $pageParser; |
110
|
|
|
$this->htaccess = $htaccess; |
111
|
|
|
$this->metaConfig = $metaConfig; |
112
|
|
|
$this->async = $async; |
113
|
|
|
$this->environment = $environment; |
114
|
|
|
$this->siteMap = $siteMap; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Load a site from YAML configuration files in the `directories.src`/site directory. |
119
|
|
|
* All YAML files are loaded and parsed into Page objects and added to a Site collection. |
120
|
|
|
* |
121
|
|
|
* @param array $routes |
122
|
|
|
* |
123
|
|
|
* @return Site |
124
|
|
|
* @throws InvalidSiteException |
125
|
|
|
* @see \Brendt\Stitcher\Site\Page |
126
|
|
|
* @see \Brendt\Stitcher\Site\Site |
127
|
|
|
*/ |
128
|
|
|
public function loadSite(array $routes = []) : Site { |
129
|
|
|
/** @var SplFileInfo[] $files */ |
130
|
|
|
$files = Finder::create()->files()->in("{$this->srcDir}/site")->name('*.yml'); |
131
|
|
|
$site = new Site(); |
132
|
|
|
|
133
|
|
|
foreach ($files as $file) { |
134
|
|
|
try { |
135
|
|
|
$fileContents = (array) Yaml::parse($file->getContents()); |
136
|
|
|
} catch (ParseException $e) { |
137
|
|
|
throw new InvalidSiteException("{$file->getRelativePathname()}: {$e->getMessage()}"); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
foreach ($fileContents as $route => $config) { |
141
|
|
|
if (count($routes) && !in_array($route, $routes)) { |
142
|
|
|
continue; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$this->loadPage($site, $route, $config); |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
return $site; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @param Site $site |
154
|
|
|
* @param string $route |
155
|
|
|
* @param array $config |
156
|
|
|
*/ |
157
|
|
|
private function loadPage(Site $site, string $route, array $config) { |
158
|
|
|
if (isset($config[self::TOKEN_REDIRECT])) { |
159
|
|
|
$this->htaccess->addRedirect($route, $config[self::TOKEN_REDIRECT]); |
160
|
|
|
|
161
|
|
|
return; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$page = new Page($route, $config, $this->createMeta()); |
165
|
|
|
$site->addPage($page); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Parse a path into usable data. |
170
|
|
|
* |
171
|
|
|
* @param array $routes |
172
|
|
|
* @param string $filterValue |
173
|
|
|
* |
174
|
|
|
* @return array |
175
|
|
|
* @throws TemplateNotFoundException |
176
|
|
|
*/ |
177
|
|
|
public function parse($routes = [], string $filterValue = null) { |
178
|
|
|
$blanket = []; |
179
|
|
|
$manager = extension_loaded('pcntl') && $this->async ? new Manager() : null; |
180
|
|
|
$processCollection = new ProcessCollection(); |
181
|
|
|
|
182
|
|
|
$site = $this->loadSite((array) $routes); |
183
|
|
|
$this->eventDispatcher->dispatch(self::EVENT_PARSER_INIT, Event::create(['site' => $site])); |
184
|
|
|
|
185
|
|
|
foreach ($site as $page) { |
186
|
|
|
$pageRenderProcess = $this->createPageRenderProcess($page, $filterValue); |
187
|
|
|
$this->eventDispatcher->dispatch(self::EVENT_PAGE_PARSING, Event::create(['page' => $page])); |
188
|
|
|
|
189
|
|
|
if ($manager) { |
190
|
|
|
$pageRenderProcess->setAsync(); |
191
|
|
|
$processCollection[] = $manager->async($pageRenderProcess); |
192
|
|
|
} else { |
193
|
|
|
$blanket += $pageRenderProcess->execute(); |
194
|
|
|
$pageRenderProcess->triggerSuccess(); |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
if ($manager) { |
199
|
|
|
$manager->wait($processCollection); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
return $blanket; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Create a page render process |
207
|
|
|
* |
208
|
|
|
* @param Page $page |
209
|
|
|
* @param string|null $filterValue |
210
|
|
|
* |
211
|
|
|
* @return PageRenderProcess |
212
|
|
|
*/ |
213
|
|
|
private function createPageRenderProcess(Page $page, string $filterValue = null) : PageRenderProcess { |
214
|
|
|
$pageRenderProcess = new PageRenderProcess($this->pageParser, $page, $this->publicDir, $filterValue); |
215
|
|
|
$pageRenderProcess->setEnvironment($this->environment); |
216
|
|
|
|
217
|
|
|
if ($this->siteMap->isEnabled()) { |
218
|
|
|
$pageRenderProcess->setSiteMap($this->siteMap); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$pageRenderProcess->onSuccess(function () use ($page) { |
222
|
|
|
$this->eventDispatcher->dispatch(SiteParser::EVENT_PAGE_PARSED, Event::create(['pageId' => $page->getId()])); |
223
|
|
|
}); |
224
|
|
|
|
225
|
|
|
return $pageRenderProcess; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @param string $filter |
230
|
|
|
* |
231
|
|
|
* @return SiteParser |
232
|
|
|
*/ |
233
|
|
|
public function setFilter(string $filter) : SiteParser { |
234
|
|
|
$this->filter = $filter; |
235
|
|
|
|
236
|
|
|
return $this; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* @return Meta |
241
|
|
|
*/ |
242
|
|
|
private function createMeta() : Meta { |
243
|
|
|
$meta = new Meta(); |
244
|
|
|
|
245
|
|
|
foreach ($this->metaConfig as $name => $value) { |
246
|
|
|
$meta->name($name, $value); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
return $meta; |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
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.