Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
80 / 80 |
|
100.00% |
20 / 20 |
CRAP | |
100.00% |
1 / 1 |
Stream | |
100.00% |
80 / 80 |
|
100.00% |
20 / 20 |
41 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
setTemplate | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setMaster | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getBlocks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBlock | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMasterBlocks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMasterBlock | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setBlocks | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setBlock | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setMasterBlocks | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setMasterBlock | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getParent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMaster | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isFile | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
render | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
parseParent | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
parseIncludes | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
parseBlocks | |
100.00% |
26 / 26 |
|
100.00% |
1 / 1 |
8 | |||
renderTemplate | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\View\Template; |
15 | |
16 | /** |
17 | * View stream template class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\View |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 4.0.0 |
25 | */ |
26 | class Stream extends AbstractTemplate |
27 | { |
28 | |
29 | /** |
30 | * View template file |
31 | * @var ?string |
32 | */ |
33 | protected ?string $file = null; |
34 | |
35 | /** |
36 | * View parent template |
37 | * @var ?Stream |
38 | */ |
39 | protected ?Stream $parent = null; |
40 | |
41 | /** |
42 | * Block templates |
43 | * @var array |
44 | */ |
45 | protected array $blocks = []; |
46 | |
47 | /** |
48 | * Master template |
49 | * @var ?string |
50 | */ |
51 | protected ?string $master = null; |
52 | |
53 | /** |
54 | * Master block templates |
55 | * @var array |
56 | */ |
57 | protected array $masterBlocks = []; |
58 | |
59 | /** |
60 | * Constructor |
61 | * |
62 | * Instantiate the view stream template object |
63 | * |
64 | * @param string $template |
65 | */ |
66 | public function __construct(string $template) |
67 | { |
68 | $this->setTemplate($template); |
69 | |
70 | // Parse parent template |
71 | $this->parseParent(); |
72 | |
73 | // Parse includes |
74 | $this->parseIncludes(); |
75 | |
76 | // Parse blocks |
77 | $this->parseBlocks(); |
78 | } |
79 | |
80 | /** |
81 | * Set view template with auto-detect |
82 | * |
83 | * @param string $template |
84 | * @return static |
85 | */ |
86 | public function setTemplate(string $template): static |
87 | { |
88 | if ((strlen($template) <= 255) && file_exists($template)) { |
89 | $this->template = file_get_contents($template); |
90 | $this->file = $template; |
91 | } else { |
92 | $this->template = $template; |
93 | } |
94 | return $this; |
95 | } |
96 | |
97 | /** |
98 | * Set master |
99 | * |
100 | * @param string $master |
101 | * @return static |
102 | */ |
103 | public function setMaster(string $master): static |
104 | { |
105 | $this->master = $master; |
106 | return $this; |
107 | } |
108 | |
109 | /** |
110 | * Get blocks |
111 | * |
112 | * @return array |
113 | */ |
114 | public function getBlocks(): array |
115 | { |
116 | return $this->blocks; |
117 | } |
118 | |
119 | /** |
120 | * Get block by name |
121 | * |
122 | * @param string $name |
123 | * @return string|null |
124 | */ |
125 | public function getBlock(string $name): string|null |
126 | { |
127 | return $this->blocks[$name] ?? null; |
128 | } |
129 | |
130 | /** |
131 | * Get master blocks |
132 | * |
133 | * @return array |
134 | */ |
135 | public function getMasterBlocks(): array |
136 | { |
137 | return $this->masterBlocks; |
138 | } |
139 | |
140 | /** |
141 | * Get master block by name |
142 | * |
143 | * @param string $name |
144 | * @return string|null |
145 | */ |
146 | public function getMasterBlock(string $name): string|null |
147 | { |
148 | return $this->masterBlocks[$name] ?? null; |
149 | } |
150 | |
151 | /** |
152 | * Set blocks |
153 | * |
154 | * @param array $blocks |
155 | * @return static |
156 | */ |
157 | public function setBlocks(array $blocks): static |
158 | { |
159 | $this->blocks = $blocks; |
160 | return $this; |
161 | } |
162 | |
163 | /** |
164 | * Set block |
165 | * |
166 | * @param string $name |
167 | * @param string $value |
168 | * @return static |
169 | */ |
170 | public function setBlock($name, $value): static |
171 | { |
172 | $this->blocks[$name] = $value; |
173 | return $this; |
174 | } |
175 | |
176 | /** |
177 | * Set master blocks |
178 | * |
179 | * @param array $blocks |
180 | * @return static |
181 | */ |
182 | public function setMasterBlocks(array $blocks): static |
183 | { |
184 | $this->masterBlocks = $blocks; |
185 | return $this; |
186 | } |
187 | |
188 | /** |
189 | * Set master block |
190 | * |
191 | * @param string $name |
192 | * @param string $value |
193 | * @return static |
194 | */ |
195 | public function setMasterBlock(string $name, string $value): static |
196 | { |
197 | $this->masterBlocks[$name] = $value; |
198 | return $this; |
199 | } |
200 | |
201 | /** |
202 | * Get parent |
203 | * |
204 | * @return static|null |
205 | */ |
206 | public function getParent(): static|null |
207 | { |
208 | return $this->parent; |
209 | } |
210 | |
211 | /** |
212 | * Get master |
213 | * |
214 | * @return string |
215 | */ |
216 | public function getMaster(): string |
217 | { |
218 | return $this->master; |
219 | } |
220 | |
221 | /** |
222 | * Determine if the template stream is from a file |
223 | * |
224 | * @return bool |
225 | */ |
226 | public function isFile(): bool |
227 | { |
228 | return ($this->file !== null); |
229 | } |
230 | |
231 | /** |
232 | * Determine if the template stream is from a string |
233 | * |
234 | * @return bool |
235 | */ |
236 | public function isString(): bool |
237 | { |
238 | return ($this->file === null); |
239 | } |
240 | |
241 | /** |
242 | * Render the view and return the output |
243 | * |
244 | * @param ?array $data |
245 | * @return string |
246 | */ |
247 | public function render(?array $data = null): string |
248 | { |
249 | if ($data !== null) { |
250 | $this->data = $data; |
251 | } |
252 | $this->renderTemplate(); |
253 | return $this->output; |
254 | } |
255 | |
256 | /** |
257 | * Parse template parent/child blocks |
258 | * |
259 | * @return void |
260 | */ |
261 | protected function parseParent(): void |
262 | { |
263 | $matches = []; |
264 | preg_match_all('/\{\{\@extends(.*?)\}\}/s', $this->template, $matches); |
265 | |
266 | if (isset($matches[0]) && isset($matches[0][0])) { |
267 | foreach ($matches[0] as $key => $match) { |
268 | $tmpl = trim($matches[1][$key]); |
269 | if ($tmpl != $this->file) { |
270 | $dir = ($this->isFile()) ? dirname($this->file) . DIRECTORY_SEPARATOR : null; |
271 | $this->template = str_replace($match, '', $this->template); |
272 | $this->parent = new Stream($dir . $tmpl); |
273 | } |
274 | } |
275 | } |
276 | } |
277 | |
278 | /** |
279 | * Parse template includes |
280 | * |
281 | * @return void |
282 | */ |
283 | protected function parseIncludes(): void |
284 | { |
285 | $matches = []; |
286 | preg_match_all('/\{\{\@include(.*?)\}\}/s', $this->template, $matches); |
287 | |
288 | if (isset($matches[0]) && isset($matches[0][0])) { |
289 | foreach ($matches[0] as $key => $match) { |
290 | $tmpl = trim($matches[1][$key]); |
291 | if ($tmpl != $this->file) { |
292 | $dir = ($this->isFile()) ? dirname($this->file) . DIRECTORY_SEPARATOR : null; |
293 | $view = new Stream($dir . $tmpl); |
294 | $this->template = str_replace($match, $view->render($this->data), $this->template); |
295 | } |
296 | } |
297 | } |
298 | } |
299 | |
300 | /** |
301 | * Parse template parent/child blocks |
302 | * |
303 | * @return void |
304 | */ |
305 | protected function parseBlocks(): void |
306 | { |
307 | $matches = []; |
308 | preg_match_all('/\{\{(.*?)\{\{\/(.*?)\}\}/s', $this->template, $matches); |
309 | |
310 | if (isset($matches[0]) && isset($matches[0][0])) { |
311 | foreach ($matches[0] as $match) { |
312 | $name = substr($match, 2); |
313 | $name = substr($name, 0, strpos($name, '}}')); |
314 | $content = substr($match, (strpos($match, '}}') + 2)); |
315 | $content = substr($content, 0, strpos($content, '{{/')); |
316 | $this->blocks[$name] = $content; |
317 | } |
318 | } |
319 | |
320 | $parent = $this->parent; |
321 | |
322 | if ($parent === null) { |
323 | $this->setMaster($this->template); |
324 | $this->setMasterBlocks($this->blocks); |
325 | } |
326 | |
327 | while ($parent !== null) { |
328 | $this->setMaster($parent->getMaster()); |
329 | $this->setMasterBlocks($parent->getMasterBlocks()); |
330 | |
331 | foreach ($this->blocks as $block => $tmpl) { |
332 | $this->setBlock('header', str_replace('{{parent}}', $parent->getBlock($block), $tmpl)); |
333 | } |
334 | |
335 | $parent = $parent->getParent(); |
336 | } |
337 | |
338 | $this->template = $this->master; |
339 | foreach ($this->blocks as $block => $tmpl) { |
340 | $this->template = str_replace( |
341 | '{{' . $block . '}}' . $this->getMasterBlock($block) . '{{/' . $block . '}}', |
342 | $tmpl, |
343 | $this->template |
344 | ); |
345 | } |
346 | } |
347 | |
348 | /** |
349 | * Render view template string |
350 | * |
351 | * @return void |
352 | */ |
353 | protected function renderTemplate(): void |
354 | { |
355 | if ($this->data !== null) { |
356 | $this->output = $this->template; |
357 | |
358 | // Parse array values |
359 | $this->output = Stream\Parser::parseArrays($this->template, $this->data, $this->output); |
360 | |
361 | // Parse conditionals |
362 | $this->output = Stream\Parser::parseConditionals($this->template, $this->data, $this->output); |
363 | |
364 | // Parse scalar values |
365 | $this->output = Stream\Parser::parseScalars($this->data, $this->output); |
366 | } |
367 | } |
368 | |
369 | } |