This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @author Todd Burry <[email protected]> |
||
4 | * @copyright 2009-2017 Vanilla Forums Inc. |
||
5 | * @license MIT |
||
6 | */ |
||
7 | |||
8 | namespace Ebi; |
||
9 | |||
10 | use DOMAttr; |
||
11 | use DOMElement; |
||
12 | use DOMNode; |
||
13 | use Symfony\Component\ExpressionLanguage\SyntaxError; |
||
14 | |||
15 | class Compiler { |
||
16 | const T_IF = 'x-if'; |
||
17 | const T_EACH = 'x-each'; |
||
18 | const T_WITH = 'x-with'; |
||
19 | const T_LITERAL = 'x-literal'; |
||
20 | const T_AS = 'x-as'; |
||
21 | const T_COMPONENT = 'x-component'; |
||
22 | const T_CHILDREN = 'x-children'; |
||
23 | const T_BLOCK = 'x-block'; |
||
24 | const T_ELSE = 'x-else'; |
||
25 | const T_EMPTY = 'x-empty'; |
||
26 | const T_X = 'x'; |
||
27 | const T_INCLUDE = 'x-include'; |
||
28 | const T_EBI = 'ebi'; |
||
29 | const T_UNESCAPE = 'x-unescape'; |
||
30 | const T_TAG = 'x-tag'; |
||
31 | |||
32 | const IDENT_REGEX = '`^([a-z0-9-]+)$`i'; |
||
33 | |||
34 | protected static $special = [ |
||
35 | self::T_COMPONENT => 1, |
||
36 | self::T_IF => 2, |
||
37 | self::T_ELSE => 3, |
||
38 | self::T_EACH => 4, |
||
39 | self::T_EMPTY => 5, |
||
40 | self::T_CHILDREN => 6, |
||
41 | self::T_INCLUDE => 7, |
||
42 | self::T_WITH => 8, |
||
43 | self::T_BLOCK => 9, |
||
44 | self::T_LITERAL => 10, |
||
45 | self::T_AS => 11, |
||
46 | self::T_UNESCAPE => 12, |
||
47 | self::T_TAG => 13 |
||
48 | ]; |
||
49 | |||
50 | protected static $htmlTags = [ |
||
51 | 'a' => 'i', |
||
52 | 'abbr' => 'i', |
||
53 | 'acronym' => 'i', // deprecated |
||
54 | 'address' => 'b', |
||
55 | // 'applet' => 'i', // deprecated |
||
0 ignored issues
–
show
|
|||
56 | 'area' => 'i', |
||
57 | 'article' => 'b', |
||
58 | 'aside' => 'b', |
||
59 | 'audio' => 'i', |
||
60 | 'b' => 'i', |
||
61 | 'base' => 'i', |
||
62 | // 'basefont' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
63 | 'bdi' => 'i', |
||
64 | 'bdo' => 'i', |
||
65 | // 'bgsound' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
54% 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. ![]() |
|||
66 | // 'big' => 'i', |
||
67 | 'x' => 'i', |
||
68 | // 'blink' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
69 | 'blockquote' => 'b', |
||
70 | 'body' => 'b', |
||
71 | 'br' => 'i', |
||
72 | 'button' => 'i', |
||
73 | 'canvas' => 'b', |
||
74 | 'caption' => 'i', |
||
75 | // 'center' => 'b', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
76 | 'cite' => 'i', |
||
77 | 'code' => 'i', |
||
78 | 'col' => 'i', |
||
79 | 'colgroup' => 'i', |
||
80 | // 'command' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
81 | 'content' => 'i', |
||
82 | 'data' => 'i', |
||
83 | 'datalist' => 'i', |
||
84 | 'dd' => 'b', |
||
85 | 'del' => 'i', |
||
86 | 'details' => 'i', |
||
87 | 'dfn' => 'i', |
||
88 | 'dialog' => 'i', |
||
89 | // 'dir' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
90 | 'div' => 'i', |
||
91 | 'dl' => 'b', |
||
92 | 'dt' => 'b', |
||
93 | // 'element' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
94 | 'em' => 'i', |
||
95 | 'embed' => 'i', |
||
96 | 'fieldset' => 'b', |
||
97 | 'figcaption' => 'b', |
||
98 | 'figure' => 'b', |
||
99 | // 'font' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
100 | 'footer' => 'b', |
||
101 | 'form' => 'b', |
||
102 | 'frame' => 'i', |
||
103 | 'frameset' => 'i', |
||
104 | 'h1' => 'b', |
||
105 | 'h2' => 'b', |
||
106 | 'h3' => 'b', |
||
107 | 'h4' => 'b', |
||
108 | 'h5' => 'b', |
||
109 | 'h6' => 'b', |
||
110 | 'head' => 'b', |
||
111 | 'header' => 'b', |
||
112 | 'hgroup' => 'b', |
||
113 | 'hr' => 'b', |
||
114 | 'html' => 'b', |
||
115 | 'i' => 'i', |
||
116 | 'iframe' => 'i', |
||
117 | 'image' => 'i', |
||
118 | 'img' => 'i', |
||
119 | 'input' => 'i', |
||
120 | 'ins' => 'i', |
||
121 | 'isindex' => 'i', |
||
122 | 'kbd' => 'i', |
||
123 | 'keygen' => 'i', |
||
124 | 'label' => 'i', |
||
125 | 'legend' => 'i', |
||
126 | 'li' => 'i', |
||
127 | 'link' => 'i', |
||
128 | // 'listing' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
129 | 'main' => 'b', |
||
130 | 'map' => 'i', |
||
131 | 'mark' => 'i', |
||
132 | // 'marquee' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
133 | 'menu' => 'i', |
||
134 | 'menuitem' => 'i', |
||
135 | 'meta' => 'i', |
||
136 | 'meter' => 'i', |
||
137 | 'multicol' => 'i', |
||
138 | 'nav' => 'b', |
||
139 | 'nobr' => 'i', |
||
140 | 'noembed' => 'i', |
||
141 | 'noframes' => 'i', |
||
142 | 'noscript' => 'b', |
||
143 | 'object' => 'i', |
||
144 | 'ol' => 'b', |
||
145 | 'optgroup' => 'i', |
||
146 | 'option' => 'b', |
||
147 | 'output' => 'i', |
||
148 | 'p' => 'b', |
||
149 | 'param' => 'i', |
||
150 | 'picture' => 'i', |
||
151 | // 'plaintext' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
152 | 'pre' => 'b', |
||
153 | 'progress' => 'i', |
||
154 | 'q' => 'i', |
||
155 | 'rp' => 'i', |
||
156 | 'rt' => 'i', |
||
157 | 'rtc' => 'i', |
||
158 | 'ruby' => 'i', |
||
159 | 's' => 'i', |
||
160 | 'samp' => 'i', |
||
161 | 'script' => 'i', |
||
162 | 'section' => 'b', |
||
163 | 'select' => 'i', |
||
164 | // 'shadow' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
165 | 'slot' => 'i', |
||
166 | 'small' => 'i', |
||
167 | 'source' => 'i', |
||
168 | // 'spacer' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
169 | 'span' => 'i', |
||
170 | // 'strike' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
171 | 'strong' => 'i', |
||
172 | 'style' => 'i', |
||
173 | 'sub' => 'i', |
||
174 | 'summary' => 'i', |
||
175 | 'sup' => 'i', |
||
176 | 'table' => 'b', |
||
177 | 'tbody' => 'i', |
||
178 | 'td' => 'i', |
||
179 | 'template' => 'i', |
||
180 | 'textarea' => 'i', |
||
181 | 'tfoot' => 'b', |
||
182 | 'th' => 'i', |
||
183 | 'thead' => 'i', |
||
184 | 'time' => 'i', |
||
185 | 'title' => 'i', |
||
186 | 'tr' => 'i', |
||
187 | 'track' => 'i', |
||
188 | // 'tt' => 'i', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
189 | 'u' => 'i', |
||
190 | 'ul' => 'b', |
||
191 | 'var' => 'i', |
||
192 | 'video' => 'b', |
||
193 | 'wbr' => 'i', |
||
194 | |||
195 | /// SVG /// |
||
196 | 'animate' => 's', |
||
197 | 'animateColor' => 's', |
||
198 | 'animateMotion' => 's', |
||
199 | 'animateTransform' => 's', |
||
200 | // 'canvas' => 's', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
201 | 'circle' => 's', |
||
202 | 'desc' => 's', |
||
203 | 'defs' => 's', |
||
204 | 'discard' => 's', |
||
205 | 'ellipse' => 's', |
||
206 | 'g' => 's', |
||
207 | // 'image' => 's', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
208 | 'line' => 's', |
||
209 | 'marker' => 's', |
||
210 | 'mask' => 's', |
||
211 | 'missing-glyph' => 's', |
||
212 | 'mpath' => 's', |
||
213 | 'metadata' => 's', |
||
214 | 'path' => 's', |
||
215 | 'pattern' => 's', |
||
216 | 'polygon' => 's', |
||
217 | 'polyline' => 's', |
||
218 | 'rect' => 's', |
||
219 | 'set' => 's', |
||
220 | 'svg' => 's', |
||
221 | 'switch' => 's', |
||
222 | 'symbol' => 's', |
||
223 | 'text' => 's', |
||
224 | // 'unknown' => 's', |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
225 | 'use' => 's', |
||
226 | ]; |
||
227 | |||
228 | protected static $boolAttributes = [ |
||
229 | 'checked' => 1, |
||
230 | 'itemscope' => 1, |
||
231 | 'required' => 1, |
||
232 | 'selected' => 1, |
||
233 | ]; |
||
234 | |||
235 | /** |
||
236 | * @var ExpressionLanguage |
||
237 | */ |
||
238 | protected $expressions; |
||
239 | |||
240 | 86 | public function __construct() { |
|
241 | 86 | $this->expressions = new ExpressionLanguage(); |
|
242 | 86 | $this->expressions->setNamePattern('/[@a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'); |
|
243 | 86 | $this->expressions->register( |
|
244 | 86 | 'hasChildren', |
|
245 | function ($name = null) { |
||
246 | 1 | return empty($name) ? 'isset($children[0])' : "isset(\$children[$name ?: 0])"; |
|
247 | 86 | }, |
|
248 | function ($name = null) { |
||
249 | return false; |
||
250 | 86 | }); |
|
251 | 86 | } |
|
252 | |||
253 | /** |
||
254 | * Register a runtime function. |
||
255 | * |
||
256 | * @param string $name The name of the function. |
||
257 | * @param callable $function The function callback. |
||
258 | */ |
||
259 | 84 | public function defineFunction($name, $function = null) { |
|
260 | 84 | if ($function === null) { |
|
261 | 1 | $function = $name; |
|
262 | 1 | } |
|
263 | |||
264 | 84 | $this->expressions->register( |
|
265 | 84 | $name, |
|
266 | 84 | $this->getFunctionCompiler($name, $function), |
|
267 | 84 | $this->getFunctionEvaluator($function) |
|
268 | 84 | ); |
|
269 | 84 | } |
|
270 | |||
271 | 84 | private function getFunctionEvaluator($function) { |
|
272 | 84 | if ($function === 'empty') { |
|
273 | return function ($expr) { |
||
274 | return empty($expr); |
||
275 | 84 | }; |
|
276 | 83 | } elseif ($function === 'isset') { |
|
277 | return function ($expr) { |
||
278 | return isset($expr); |
||
279 | }; |
||
280 | } |
||
281 | |||
282 | 83 | return $function; |
|
283 | } |
||
284 | |||
285 | 84 | private function getFunctionCompiler($name, $function) { |
|
286 | 84 | $var = var_export(strtolower($name), true); |
|
287 | $fn = function (...$args) use ($var) { |
||
288 | 2 | return "\$this->call($var, ".implode(', ', $args).')'; |
|
289 | 84 | }; |
|
290 | |||
291 | 84 | if (is_string($function)) { |
|
292 | $fn = function (...$args) use ($function) { |
||
293 | 16 | return $function.'('.implode(', ', $args).')'; |
|
294 | 84 | }; |
|
295 | 84 | } elseif (is_array($function)) { |
|
296 | 83 | if (is_string($function[0])) { |
|
297 | $fn = function (...$args) use ($function) { |
||
298 | return "$function[0]::$function[1](".implode(', ', $args).')'; |
||
299 | }; |
||
300 | 83 | } elseif ($function[0] instanceof Ebi) { |
|
301 | $fn = function (...$args) use ($function) { |
||
302 | 17 | return "\$this->$function[1](".implode(', ', $args).')'; |
|
303 | 83 | }; |
|
304 | 83 | } |
|
305 | 83 | } |
|
306 | |||
307 | 84 | return $fn; |
|
308 | 1 | } |
|
309 | |||
310 | 78 | public function compile($src, array $options = []) { |
|
311 | 78 | $options += ['basename' => '', 'path' => '', 'runtime' => true]; |
|
312 | |||
313 | 78 | $src = trim($src); |
|
314 | |||
315 | 78 | $out = new CompilerBuffer(); |
|
316 | |||
317 | 78 | $out->setBasename($options['basename']); |
|
318 | 78 | $out->setSource($src); |
|
319 | 78 | $out->setPath($options['path']); |
|
320 | |||
321 | 78 | $dom = new \DOMDocument(); |
|
322 | |||
323 | 78 | $fragment = false; |
|
324 | 78 | if (strpos($src, '<html') === false) { |
|
325 | 76 | $src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><html><body>$src</body></html>"; |
|
326 | 76 | $fragment = true; |
|
327 | 76 | } |
|
328 | |||
329 | 78 | libxml_use_internal_errors(true); |
|
330 | 78 | $dom->loadHTML($src, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOCDATA | LIBXML_NOXMLDECL); |
|
331 | // $arr = $this->domToArray($dom); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
59% 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. ![]() |
|||
332 | |||
333 | 78 | if ($options['runtime']) { |
|
334 | 77 | $name = var_export($options['basename'], true); |
|
335 | 77 | $out->appendCode("\$this->defineComponent($name, function (\$props = [], \$children = []) {\n"); |
|
336 | 77 | } else { |
|
337 | 1 | $out->appendCode("function (\$props = [], \$children = []) {\n"); |
|
338 | } |
||
339 | |||
340 | 78 | $out->pushScope(['this' => 'props']); |
|
341 | 78 | $out->indent(+1); |
|
342 | |||
343 | 78 | $parent = $fragment ? $dom->firstChild->nextSibling->firstChild : $dom; |
|
344 | |||
345 | 78 | foreach ($parent->childNodes as $node) { |
|
346 | 78 | $this->compileNode($node, $out); |
|
347 | 70 | } |
|
348 | |||
349 | 70 | $out->indent(-1); |
|
350 | 70 | $out->popScope(); |
|
351 | |||
352 | 70 | if ($options['runtime']) { |
|
353 | 69 | $out->appendCode("});"); |
|
354 | 69 | } else { |
|
355 | 1 | $out->appendCode("};"); |
|
356 | } |
||
357 | |||
358 | 70 | $r = $out->flush(); |
|
359 | |||
360 | 70 | $errs = libxml_get_errors(); |
|
361 | |||
362 | 70 | return $r; |
|
363 | } |
||
364 | |||
365 | 57 | protected function isComponent($tag) { |
|
366 | 57 | return !isset(static::$htmlTags[$tag]); |
|
367 | } |
||
368 | |||
369 | 78 | protected function compileNode(DOMNode $node, CompilerBuffer $out) { |
|
370 | 78 | if ($out->getNodeProp($node, 'skip')) { |
|
371 | 4 | return; |
|
372 | } |
||
373 | |||
374 | 78 | switch ($node->nodeType) { |
|
375 | 78 | case XML_TEXT_NODE: |
|
376 | 64 | $this->compileTextNode($node, $out); |
|
377 | 64 | break; |
|
378 | 73 | case XML_ELEMENT_NODE: |
|
379 | /* @var \DOMElement $node */ |
||
380 | 73 | $this->compileElementNode($node, $out); |
|
381 | 66 | break; |
|
382 | 4 | case XML_COMMENT_NODE: |
|
383 | /* @var \DOMComment $node */ |
||
384 | 1 | $this->compileCommentNode($node, $out); |
|
385 | 1 | break; |
|
386 | 3 | case XML_DOCUMENT_TYPE_NODE: |
|
387 | 1 | $out->echoLiteral("<!DOCTYPE {$node->name}>\n"); |
|
388 | 1 | break; |
|
389 | 2 | case XML_CDATA_SECTION_NODE: |
|
390 | 2 | $this->compileTextNode($node, $out); |
|
391 | 2 | break; |
|
392 | default: |
||
393 | $r = "// Unknown node\n". |
||
394 | '// '.str_replace("\n", "\n// ", $node->ownerDocument->saveHTML($node)); |
||
395 | 72 | } |
|
396 | 72 | } |
|
397 | |||
398 | protected function domToArray(DOMNode $root) { |
||
399 | $result = []; |
||
400 | |||
401 | if ($root->hasAttributes()) { |
||
402 | $attrs = $root->attributes; |
||
403 | foreach ($attrs as $attr) { |
||
404 | $result['@attributes'][$attr->name] = $attr->value; |
||
405 | } |
||
406 | } |
||
407 | |||
408 | if ($root->hasChildNodes()) { |
||
409 | $children = $root->childNodes; |
||
410 | if ($children->length == 1) { |
||
411 | $child = $children->item(0); |
||
412 | if ($child->nodeType == XML_TEXT_NODE) { |
||
413 | $result['_value'] = $child->nodeValue; |
||
414 | return count($result) == 1 |
||
415 | ? $result['_value'] |
||
416 | : $result; |
||
417 | } |
||
418 | } |
||
419 | $groups = []; |
||
420 | foreach ($children as $child) { |
||
421 | if (!isset($result[$child->nodeName])) { |
||
422 | $result[$child->nodeName] = $this->domToArray($child); |
||
423 | } else { |
||
424 | if (!isset($groups[$child->nodeName])) { |
||
425 | $result[$child->nodeName] = [$result[$child->nodeName]]; |
||
426 | $groups[$child->nodeName] = 1; |
||
427 | } |
||
428 | $result[$child->nodeName][] = $this->domToArray($child); |
||
429 | } |
||
430 | } |
||
431 | } |
||
432 | |||
433 | return $result; |
||
434 | } |
||
435 | |||
436 | 1 | protected function newline(DOMNode $node, CompilerBuffer $out) { |
|
437 | 1 | if ($node->previousSibling && $node->previousSibling->nodeType !== XML_COMMENT_NODE) { |
|
438 | $out->appendCode("\n"); |
||
439 | } |
||
440 | 1 | } |
|
441 | |||
442 | 1 | protected function compileCommentNode(\DOMComment $node, CompilerBuffer $out) { |
|
443 | 1 | $comments = explode("\n", trim($node->nodeValue)); |
|
444 | |||
445 | 1 | $this->newline($node, $out); |
|
446 | 1 | foreach ($comments as $comment) { |
|
447 | 1 | $out->appendCode("// $comment\n"); |
|
448 | 1 | } |
|
449 | 1 | } |
|
450 | |||
451 | 66 | protected function compileTextNode(DOMNode $node, CompilerBuffer $out) { |
|
452 | 66 | $nodeText = $node->nodeValue; |
|
453 | |||
454 | 66 | $items = $this->splitExpressions($nodeText); |
|
455 | 66 | foreach ($items as $i => list($text, $offset)) { |
|
456 | 66 | if (preg_match('`^{\S`', $text)) { |
|
457 | 34 | if (preg_match('`^{\s*unescape\((.+)\)\s*}$`', $text, $m)) { |
|
458 | 4 | $out->echoCode($this->expr($m[1], $out)); |
|
459 | 4 | } else { |
|
460 | try { |
||
461 | 31 | $expr = substr($text, 1, -1); |
|
462 | 31 | $out->echoCode($this->compileEscape($this->expr($expr, $out))); |
|
463 | 31 | } catch (SyntaxError $ex) { |
|
464 | 1 | $nodeLineCount = substr_count($nodeText, "\n"); |
|
465 | 1 | $offsetLineCount = substr_count($nodeText, "\n", 0, $offset); |
|
466 | 1 | $line = $node->getLineNo() - $nodeLineCount + $offsetLineCount; |
|
467 | 1 | throw $out->createCompilerException($node, $ex, ['source' => $expr, 'line' => $line]); |
|
468 | } |
||
469 | } |
||
470 | 33 | } else { |
|
471 | 66 | if ($i === 0) { |
|
472 | 66 | $text = $this->ltrim($text, $node, $out); |
|
473 | 66 | } |
|
474 | 66 | if ($i === count($items) - 1) { |
|
475 | 66 | $text = $this->rtrim($text, $node, $out); |
|
476 | 66 | } |
|
477 | |||
478 | 66 | $out->echoLiteral($text); |
|
479 | } |
||
480 | 66 | } |
|
481 | 66 | } |
|
482 | |||
483 | 73 | protected function compileElementNode(DOMElement $node, CompilerBuffer $out) { |
|
484 | 73 | list($attributes, $special) = $this->splitAttributes($node); |
|
485 | |||
486 | 73 | if ($node->tagName === 'script' && ((isset($attributes['type']) && $attributes['type']->value === self::T_EBI) || !empty($special[self::T_AS]) || !empty($special[self::T_UNESCAPE]))) { |
|
487 | 10 | $this->compileExpressionNode($node, $attributes, $special, $out); |
|
488 | 71 | } elseif (!empty($special) || $this->isComponent($node->tagName)) { |
|
489 | 46 | $this->compileSpecialNode($node, $attributes, $special, $out); |
|
490 | 41 | } else { |
|
491 | 35 | $this->compileBasicElement($node, $attributes, $special, $out); |
|
492 | } |
||
493 | 66 | } |
|
494 | |||
495 | 66 | protected function splitExpressions($value) { |
|
496 | 66 | $values = preg_split('`({\S[^}]*?})`', $value, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE); |
|
497 | 66 | return $values; |
|
498 | } |
||
499 | |||
500 | /** |
||
501 | * Compile the PHP for an expression |
||
502 | * |
||
503 | * @param string $expr The expression to compile. |
||
504 | * @param CompilerBuffer $out The current output buffer. |
||
505 | * @param DOMAttr|null $attr The owner of the expression. |
||
506 | * @return string Returns a string of PHP code. |
||
507 | */ |
||
508 | 70 | protected function expr($expr, CompilerBuffer $out, DOMAttr $attr = null) { |
|
509 | 70 | $names = $out->getScopeVariables(); |
|
510 | |||
511 | try { |
||
512 | $compiled = $this->expressions->compile($expr, function ($name) use ($names) { |
||
513 | 63 | if (isset($names[$name])) { |
|
514 | 38 | return $names[$name]; |
|
515 | 39 | } elseif ($name[0] === '@') { |
|
516 | 1 | return 'this->meta['.var_export(substr($name, 1), true).']'; |
|
517 | } else { |
||
518 | 38 | return $names['this'].'['.var_export($name, true).']'; |
|
519 | } |
||
520 | 70 | }); |
|
521 | 70 | } catch (SyntaxError $ex) { |
|
522 | 3 | if ($attr !== null) { |
|
523 | 1 | throw $out->createCompilerException($attr, $ex); |
|
524 | } else { |
||
525 | 2 | throw $ex; |
|
526 | } |
||
527 | } |
||
528 | |||
529 | 67 | if ($attr !== null && null !== $fn = $this->getAttributeFunction($attr)) { |
|
530 | 10 | $compiled = call_user_func($fn, $compiled); |
|
531 | 10 | } |
|
532 | |||
533 | 67 | return $compiled; |
|
534 | } |
||
535 | |||
536 | /** |
||
537 | * Get the compiler function to wrap an attribute. |
||
538 | * |
||
539 | * Attribute functions are regular expression functions, but with a special naming convention. The following naming |
||
540 | * conventions are supported: |
||
541 | * |
||
542 | * - **@tag:attribute**: Applies to an attribute only on a specific tag. |
||
543 | * - **@attribute**: Applies to all attributes with a given name. |
||
544 | * |
||
545 | * @param DOMAttr $attr The attribute to look at. |
||
546 | * @return callable|null A function or **null** if the attribute doesn't have a function. |
||
547 | */ |
||
548 | 41 | private function getAttributeFunction(DOMAttr $attr) { |
|
549 | 41 | $keys = ['@'.$attr->ownerElement->tagName.':'.$attr->name, '@'.$attr->name]; |
|
550 | |||
551 | 41 | foreach ($keys as $key) { |
|
552 | 41 | if (null !== $fn = $this->expressions->getFunctionCompiler($key)) { |
|
553 | 17 | return $fn; |
|
554 | } |
||
555 | 41 | } |
|
556 | 31 | } |
|
557 | |||
558 | /** |
||
559 | * @param DOMElement $node |
||
560 | */ |
||
561 | 73 | protected function splitAttributes(DOMElement $node) { |
|
562 | 73 | $attributes = []; |
|
563 | 73 | $special = []; |
|
564 | |||
565 | 73 | foreach ($node->attributes as $name => $attribute) { |
|
566 | 69 | if (isset(static::$special[$name])) { |
|
567 | 50 | $special[$name] = $attribute; |
|
568 | 50 | } else { |
|
569 | 33 | $attributes[$name] = $attribute; |
|
570 | } |
||
571 | 73 | } |
|
572 | |||
573 | uksort($special, function ($a, $b) { |
||
574 | 13 | return strnatcmp(static::$special[$a], static::$special[$b]); |
|
575 | 73 | }); |
|
576 | |||
577 | 73 | return [$attributes, $special]; |
|
578 | } |
||
579 | |||
580 | 46 | protected function compileSpecialNode(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
581 | 46 | $specialName = key($special); |
|
582 | |||
583 | switch ($specialName) { |
||
584 | 46 | case self::T_COMPONENT: |
|
585 | 10 | $this->compileComponentRegister($node, $attributes, $special, $out); |
|
586 | 10 | break; |
|
587 | 46 | case self::T_IF: |
|
588 | 11 | $this->compileIf($node, $attributes, $special, $out); |
|
589 | 10 | break; |
|
590 | 45 | case self::T_EACH: |
|
591 | 16 | $this->compileEach($node, $attributes, $special, $out); |
|
592 | 15 | break; |
|
593 | 33 | case self::T_BLOCK: |
|
594 | 3 | $this->compileBlock($node, $attributes, $special, $out); |
|
595 | 2 | break; |
|
596 | 32 | case self::T_CHILDREN: |
|
597 | 4 | $this->compileChildBlock($node, $attributes, $special, $out); |
|
598 | 4 | break; |
|
599 | 32 | case self::T_INCLUDE: |
|
600 | 1 | $this->compileComponentInclude($node, $attributes, $special, $out); |
|
601 | 1 | break; |
|
602 | 32 | case self::T_WITH: |
|
603 | 5 | if ($this->isComponent($node->tagName)) { |
|
604 | // With has a special meaning in components. |
||
605 | 3 | $this->compileComponentInclude($node, $attributes, $special, $out); |
|
606 | 3 | } else { |
|
607 | 2 | $this->compileWith($node, $attributes, $special, $out); |
|
608 | } |
||
609 | 4 | break; |
|
610 | 30 | case self::T_LITERAL: |
|
611 | 2 | $this->compileLiteral($node, $attributes, $special, $out); |
|
612 | 2 | break; |
|
613 | 28 | case '': |
|
614 | 24 | if ($this->isComponent($node->tagName)) { |
|
615 | 10 | $this->compileComponentInclude($node, $attributes, $special, $out); |
|
616 | 9 | } else { |
|
617 | 22 | $this->compileElement($node, $attributes, $special, $out); |
|
618 | } |
||
619 | 23 | break; |
|
620 | 5 | case self::T_TAG: |
|
621 | 5 | default: |
|
622 | // This is only a tag node so it just gets compiled as an element. |
||
623 | 5 | $this->compileBasicElement($node, $attributes, $special, $out); |
|
624 | 5 | break; |
|
625 | } |
||
626 | 41 | } |
|
627 | |||
628 | /** |
||
629 | * Compile component registering. |
||
630 | * |
||
631 | * @param DOMElement $node |
||
632 | * @param $attributes |
||
633 | * @param $special |
||
634 | * @param CompilerBuffer $out |
||
635 | */ |
||
636 | 10 | public function compileComponentRegister(DOMElement $node, $attributes, $special, CompilerBuffer $out) { |
|
637 | 10 | $name = strtolower($special[self::T_COMPONENT]->value); |
|
638 | 10 | unset($special[self::T_COMPONENT]); |
|
639 | |||
640 | 10 | $prev = $out->select($name); |
|
641 | |||
642 | 10 | $varName = var_export($name, true); |
|
643 | 10 | $out->appendCode("\$this->defineComponent($varName, function (\$props = [], \$children = []) {\n"); |
|
644 | 10 | $out->pushScope(['this' => 'props']); |
|
645 | 10 | $out->indent(+1); |
|
646 | |||
647 | try { |
||
648 | 10 | $this->compileSpecialNode($node, $attributes, $special, $out); |
|
649 | 10 | } finally { |
|
650 | 10 | $out->popScope(); |
|
651 | 10 | $out->indent(-1); |
|
652 | 10 | $out->appendCode("});"); |
|
653 | 10 | $out->select($prev); |
|
654 | 10 | } |
|
655 | 10 | } |
|
656 | |||
657 | 3 | private function compileBlock(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
658 | // Blocks must be direct descendants of component includes. |
||
659 | 3 | if (!$out->getNodeProp($node->parentNode, self::T_INCLUDE)) { |
|
660 | 1 | throw $out->createCompilerException($node, new \Exception("Blocks must be direct descendants of component includes.")); |
|
661 | } |
||
662 | |||
663 | 2 | $name = strtolower($special[self::T_BLOCK]->value); |
|
664 | 2 | if (empty($name)) { |
|
665 | throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("Block names cannot be empty.")); |
||
666 | } |
||
667 | 2 | if (!preg_match(self::IDENT_REGEX, $name)) { |
|
668 | throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("The block name isn't a valid identifier.")); |
||
669 | } |
||
670 | |||
671 | 2 | unset($special[self::T_BLOCK]); |
|
672 | |||
673 | 2 | $prev = $out->select($name, true); |
|
674 | |||
675 | 2 | $vars = array_filter(array_unique($out->getScopeVariables())); |
|
676 | 2 | $vars[] = 'children'; |
|
677 | 2 | $use = '$'.implode(', $', array_unique($vars)); |
|
678 | |||
679 | 2 | $out->appendCode("function () use ($use) {\n"); |
|
680 | 2 | $out->pushScope(['this' => 'props']); |
|
681 | 2 | $out->indent(+1); |
|
682 | |||
683 | try { |
||
684 | 2 | $this->compileSpecialNode($node, $attributes, $special, $out); |
|
685 | 2 | } finally { |
|
686 | 2 | $out->indent(-1); |
|
687 | 2 | $out->popScope(); |
|
688 | 2 | $out->appendCode("}"); |
|
689 | 2 | $out->select($prev); |
|
690 | 2 | } |
|
691 | |||
692 | 2 | return $out; |
|
693 | } |
||
694 | |||
695 | /** |
||
696 | * Compile component inclusion and rendering. |
||
697 | * |
||
698 | * @param DOMElement $node |
||
699 | * @param DOMAttr[] $attributes |
||
700 | * @param DOMAttr[] $special |
||
701 | * @param CompilerBuffer $out |
||
702 | */ |
||
703 | 14 | protected function compileComponentInclude(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
704 | // Mark the node as a component include. |
||
705 | 14 | $out->setNodeProp($node, self::T_INCLUDE, true); |
|
706 | |||
707 | // Generate the attributes into a property array. |
||
708 | 14 | $props = []; |
|
709 | 14 | foreach ($attributes as $name => $attribute) { |
|
710 | /* @var DOMAttr $attr */ |
||
711 | 6 | if ($this->isExpression($attribute->value)) { |
|
712 | 5 | $expr = $this->expr(substr($attribute->value, 1, -1), $out, $attribute); |
|
713 | 5 | } else { |
|
714 | 2 | $expr = var_export($attribute->value, true); |
|
715 | } |
||
716 | |||
717 | 6 | $props[] = var_export($name, true).' => '.$expr; |
|
718 | 14 | } |
|
719 | 14 | $propsStr = '['.implode(', ', $props).']'; |
|
720 | |||
721 | 14 | if (isset($special[self::T_WITH])) { |
|
722 | 3 | $withExpr = $this->expr($special[self::T_WITH]->value, $out, $special[self::T_WITH]); |
|
723 | 3 | unset($special[self::T_WITH]); |
|
724 | |||
725 | 3 | $propsStr = empty($props) ? $withExpr : $propsStr.' + (array)'.$withExpr; |
|
726 | 14 | } elseif (empty($props)) { |
|
727 | // By default the current context is passed to components. |
||
728 | 6 | $propsStr = $this->expr('this', $out); |
|
729 | 6 | } |
|
730 | |||
731 | // Compile the children blocks. |
||
732 | 14 | $blocks = $this->compileComponentBlocks($node, $out); |
|
733 | 13 | $blocksStr = $blocks->flush(); |
|
734 | |||
735 | 13 | if (isset($special[self::T_INCLUDE])) { |
|
736 | 1 | $name = $this->expr($special[self::T_INCLUDE]->value, $out, $special[self::T_INCLUDE]); |
|
737 | 1 | } else { |
|
738 | 12 | $name = var_export($node->tagName, true); |
|
739 | } |
||
740 | |||
741 | 13 | $out->appendCode("\$this->write($name, $propsStr, $blocksStr);\n"); |
|
742 | 13 | } |
|
743 | |||
744 | /** |
||
745 | * @param DOMElement $parent |
||
746 | * @return CompilerBuffer |
||
747 | */ |
||
748 | 14 | protected function compileComponentBlocks(DOMElement $parent, CompilerBuffer $out) { |
|
749 | 14 | $blocksOut = new CompilerBuffer(CompilerBuffer::STYLE_ARRAY, [ |
|
750 | 14 | 'baseIndent' => $out->getIndent(), |
|
751 | 14 | 'indent' => $out->getIndent() + 1, |
|
752 | 14 | 'depth' => $out->getDepth(), |
|
753 | 14 | 'scopes' => $out->getAllScopes(), |
|
754 | 14 | 'nodeProps' => $out->getNodePropArray() |
|
755 | 14 | ]); |
|
756 | 14 | $blocksOut->setSource($out->getSource()); |
|
757 | |||
758 | 14 | if ($this->isEmptyNode($parent)) { |
|
759 | 10 | return $blocksOut; |
|
760 | } |
||
761 | |||
762 | 5 | $use = '$'.implode(', $', $blocksOut->getScopeVariables()).', $children'; |
|
763 | |||
764 | 5 | $blocksOut->appendCode("function () use ($use) {\n"); |
|
765 | 5 | $blocksOut->indent(+1); |
|
766 | |||
767 | try { |
||
768 | 5 | foreach ($parent->childNodes as $node) { |
|
769 | 5 | $this->compileNode($node, $blocksOut); |
|
770 | 4 | } |
|
771 | 4 | } finally { |
|
772 | 5 | $blocksOut->indent(-1); |
|
773 | 5 | $blocksOut->appendCode("}"); |
|
774 | 5 | } |
|
775 | |||
776 | 4 | return $blocksOut; |
|
777 | } |
||
778 | |||
779 | /** |
||
780 | * Output the source of a node as a PHP comment. |
||
781 | * |
||
782 | * @param DOMElement $node The node to output. |
||
783 | * @param DOMAttr[] $attributes Regular attributes. |
||
784 | * @param DOMAttr[] $special Special attributes. |
||
785 | * @param CompilerBuffer $out The output buffer for the results. |
||
786 | */ |
||
787 | 34 | protected function compileTagComment(DOMElement $node, $attributes, $special, CompilerBuffer $out) { |
|
788 | // Don't double up comments. |
||
789 | 34 | if ($node->previousSibling && $node->previousSibling->nodeType === XML_COMMENT_NODE) { |
|
790 | return; |
||
791 | } |
||
792 | |||
793 | 34 | $str = '<'.$node->tagName; |
|
794 | 34 | foreach ($special as $attr) { |
|
795 | /* @var DOMAttr $attr */ |
||
796 | 34 | $str .= ' '.$attr->name.(empty($attr->value) ? '' : '="'.str_replace('"', '"', $attr->value).'"'); |
|
797 | 34 | } |
|
798 | 34 | $str .= '>'; |
|
799 | 34 | $comments = explode("\n", $str); |
|
800 | 34 | foreach ($comments as $comment) { |
|
801 | 34 | $out->appendCode("// $comment\n"); |
|
802 | 34 | } |
|
803 | 34 | } |
|
804 | |||
805 | /** |
||
806 | * @param DOMElement $node |
||
807 | * @param DOMAttr[] $attributes |
||
808 | * @param DOMAttr[] $special |
||
809 | * @param CompilerBuffer $out |
||
810 | * @param bool $force |
||
811 | */ |
||
812 | 62 | protected function compileOpenTag(DOMElement $node, $attributes, $special, CompilerBuffer $out, $force = false) { |
|
813 | 62 | $tagNameExpr = !empty($special[self::T_TAG]) ? $special[self::T_TAG]->value : ''; |
|
814 | |||
815 | 62 | if ($node->tagName === self::T_X && empty($tagNameExpr)) { |
|
816 | 6 | return; |
|
817 | } |
||
818 | |||
819 | 59 | if (!empty($tagNameExpr)) { |
|
820 | 5 | $tagNameExpr = $this->expr($tagNameExpr, $out, $special[self::T_TAG]); |
|
821 | 5 | $tagName = $node->tagName === 'x' ? "''" : var_export($node->tagName, true); |
|
822 | |||
823 | 5 | $tagVar = $out->depthName('$tag', 1); |
|
824 | 5 | if ($node->hasChildNodes() || $force) { |
|
825 | 5 | $out->setNodeProp($node, self::T_TAG, $tagVar); |
|
826 | 5 | $out->depth(+1); |
|
827 | 5 | } |
|
828 | |||
829 | 5 | $out->appendCode("\n"); |
|
830 | 5 | $this->compileTagComment($node, $attributes, $special, $out); |
|
831 | 5 | $out->appendCode("$tagVar = \$this->tagName($tagNameExpr, $tagName);\n"); |
|
832 | 5 | $out->appendCode("if ($tagVar) {\n"); |
|
833 | 5 | $out->indent(+1); |
|
834 | 5 | $out->echoLiteral('<'); |
|
835 | 5 | $out->echoCode($tagVar); |
|
836 | 5 | } else { |
|
837 | 55 | $out->echoLiteral('<'.$node->tagName); |
|
838 | } |
||
839 | |||
840 | /* @var DOMAttr $attribute */ |
||
841 | 59 | foreach ($attributes as $name => $attribute) { |
|
842 | // Check for an attribute expression. |
||
843 | 27 | if ($this->isExpression($attribute->value)) { |
|
844 | 19 | $out->echoCode( |
|
845 | 19 | '$this->attribute('.var_export($name, true).', '. |
|
846 | 19 | $this->expr(substr($attribute->value, 1, -1), $out, $attribute). |
|
847 | 19 | ')'); |
|
848 | 27 | } elseif (null !== $fn = $this->getAttributeFunction($attribute)) { |
|
849 | 7 | $value = call_user_func($fn, var_export($attribute->value, true)); |
|
850 | |||
851 | 7 | $out->echoCode('$this->attribute('.var_export($name, true).', '.$value.')'); |
|
852 | 9 | } elseif ((empty($attribute->value) || $attribute->value === $name) && isset(self::$boolAttributes[$name])) { |
|
853 | 2 | $out->echoLiteral(' '.$name); |
|
854 | 2 | } else { |
|
855 | 4 | $out->echoLiteral(' '.$name.'="'); |
|
856 | 4 | $out->echoLiteral(htmlspecialchars($attribute->value)); |
|
857 | 4 | $out->echoLiteral('"'); |
|
858 | } |
||
859 | 59 | } |
|
860 | |||
861 | 59 | if ($node->hasChildNodes() || $force) { |
|
862 | 57 | $out->echoLiteral('>'); |
|
863 | 57 | } else { |
|
864 | 3 | $out->echoLiteral(" />"); |
|
865 | } |
||
866 | |||
867 | 59 | if (!empty($tagNameExpr)) { |
|
868 | 5 | $out->indent(-1); |
|
869 | 5 | $out->appendCode("}\n\n"); |
|
870 | 5 | } |
|
871 | 59 | } |
|
872 | |||
873 | 31 | private function isExpression($value) { |
|
874 | 31 | return preg_match('`^{\S.*}$`', $value); |
|
875 | } |
||
876 | |||
877 | 60 | protected function compileCloseTag(DOMElement $node, $special, CompilerBuffer $out, $force = false) { |
|
878 | 60 | if (!$force && !$node->hasChildNodes()) { |
|
879 | 3 | return; |
|
880 | } |
||
881 | |||
882 | 58 | $tagNameExpr = $out->getNodeProp($node, self::T_TAG); //!empty($special[self::T_TAG]) ? $special[self::T_TAG]->value : ''; |
|
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
74% 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. ![]() |
|||
883 | 58 | if (!empty($tagNameExpr)) { |
|
884 | 5 | $out->appendCode("\n"); |
|
885 | 5 | $out->appendCode("if ($tagNameExpr) {\n"); |
|
886 | 5 | $out->indent(+1); |
|
887 | |||
888 | 5 | $out->echoLiteral('</'); |
|
889 | 5 | $out->echoCode($tagNameExpr); |
|
890 | 5 | $out->echoLiteral('>'); |
|
891 | 5 | $out->indent(-1); |
|
892 | 5 | $out->appendCode("}\n"); |
|
893 | 5 | $out->depth(-1); |
|
894 | 58 | } elseif ($node->tagName !== self::T_X) { |
|
895 | 51 | $out->echoLiteral("</{$node->tagName}>"); |
|
896 | 51 | } |
|
897 | 58 | } |
|
898 | |||
899 | 4 | protected function isEmptyText(DOMNode $node) { |
|
900 | 4 | return $node instanceof \DOMText && empty(trim($node->data)); |
|
901 | } |
||
902 | |||
903 | 14 | protected function isEmptyNode(DOMNode $node) { |
|
904 | 14 | if (!$node->hasChildNodes()) { |
|
905 | 10 | return true; |
|
906 | } |
||
907 | |||
908 | 5 | foreach ($node->childNodes as $childNode) { |
|
909 | 5 | if ($childNode instanceof DOMElement) { |
|
910 | 3 | return false; |
|
911 | } |
||
912 | 2 | if ($childNode instanceof \DOMText && !$this->isEmptyText($childNode)) { |
|
913 | 2 | return false; |
|
914 | } |
||
915 | } |
||
916 | |||
917 | return true; |
||
918 | } |
||
919 | |||
920 | 11 | protected function compileIf(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
921 | 11 | $this->compileTagComment($node, $attributes, $special, $out); |
|
922 | 11 | $expr = $this->expr($special[self::T_IF]->value, $out, $special[self::T_IF]); |
|
923 | 10 | unset($special[self::T_IF]); |
|
924 | |||
925 | 10 | $elseNode = $this->findSpecialNode($node, self::T_ELSE, self::T_IF); |
|
926 | 10 | $out->setNodeProp($elseNode, 'skip', true); |
|
927 | |||
928 | 10 | $out->appendCode('if ('.$expr.") {\n"); |
|
929 | 10 | $out->indent(+1); |
|
930 | |||
931 | 10 | $this->compileSpecialNode($node, $attributes, $special, $out); |
|
932 | |||
933 | 10 | $out->indent(-1); |
|
934 | |||
935 | 10 | if ($elseNode) { |
|
936 | 2 | list($attributes, $special) = $this->splitAttributes($elseNode); |
|
937 | 2 | unset($special[self::T_ELSE]); |
|
938 | |||
939 | 2 | $out->appendCode("} else {\n"); |
|
940 | |||
941 | 2 | $out->indent(+1); |
|
942 | 2 | $this->compileSpecialNode($elseNode, $attributes, $special, $out); |
|
943 | 2 | $out->indent(-1); |
|
944 | 2 | } |
|
945 | |||
946 | 10 | $out->appendCode("}\n"); |
|
947 | 10 | } |
|
948 | |||
949 | 16 | protected function compileEach(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
950 | 16 | $this->compileTagComment($node, $attributes, $special, $out); |
|
951 | 16 | $this->compileOpenTag($node, $attributes, $special, $out); |
|
952 | |||
953 | 16 | $emptyNode = $this->findSpecialNode($node, self::T_EMPTY, self::T_ELSE); |
|
954 | 16 | $out->setNodeProp($emptyNode, 'skip', true); |
|
955 | |||
956 | 16 | if ($emptyNode === null) { |
|
957 | 14 | $this->compileEachLoop($node, $attributes, $special, $out); |
|
958 | 13 | } else { |
|
959 | 2 | $expr = $this->expr("empty({$special[self::T_EACH]->value})", $out); |
|
960 | |||
961 | 2 | list ($emptyAttributes, $emptySpecial) = $this->splitAttributes($emptyNode); |
|
962 | 2 | unset($emptySpecial[self::T_EMPTY]); |
|
963 | |||
964 | 2 | $out->appendCode('if ('.$expr.") {\n"); |
|
965 | |||
966 | 2 | $out->indent(+1); |
|
967 | 2 | $this->compileSpecialNode($emptyNode, $emptyAttributes, $emptySpecial, $out); |
|
968 | 2 | $out->indent(-1); |
|
969 | |||
970 | 2 | $out->appendCode("} else {\n"); |
|
971 | |||
972 | 2 | $out->indent(+1); |
|
973 | 2 | $this->compileEachLoop($node, $attributes, $special, $out); |
|
974 | 2 | $out->indent(-1); |
|
975 | |||
976 | 2 | $out->appendCode("}\n"); |
|
977 | } |
||
978 | |||
979 | 15 | $this->compileCloseTag($node, $special, $out); |
|
980 | 15 | } |
|
981 | |||
982 | 2 | protected function compileWith(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
983 | 2 | $this->compileTagComment($node, $attributes, $special, $out); |
|
984 | |||
985 | 2 | $out->depth(+1); |
|
986 | 2 | $scope = ['this' => $out->depthName('props')]; |
|
987 | 2 | if (!empty($special[self::T_AS])) { |
|
988 | 2 | if (preg_match(self::IDENT_REGEX, $special[self::T_AS]->value, $m)) { |
|
989 | // The template specified an x-as attribute to alias the with expression. |
||
990 | 1 | $scope = [$m[1] => $out->depthName('props')]; |
|
991 | 1 | } else { |
|
992 | 1 | throw $out->createCompilerException( |
|
993 | 1 | $special[self::T_AS], |
|
994 | 1 | new \Exception("Invalid identifier in x-as attribute.") |
|
995 | 1 | ); |
|
996 | } |
||
997 | 1 | } |
|
998 | 1 | $with = $this->expr($special[self::T_WITH]->value, $out); |
|
999 | |||
1000 | 1 | unset($special[self::T_WITH], $special[self::T_AS]); |
|
1001 | |||
1002 | 1 | $out->pushScope($scope); |
|
1003 | 1 | $out->appendCode('$'.$out->depthName('props')." = $with;\n"); |
|
1004 | |||
1005 | 1 | $this->compileSpecialNode($node, $attributes, $special, $out); |
|
1006 | |||
1007 | 1 | $out->depth(-1); |
|
1008 | 1 | $out->popScope(); |
|
1009 | 1 | } |
|
1010 | |||
1011 | 2 | protected function compileLiteral(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
1012 | 2 | $this->compileTagComment($node, $attributes, $special, $out); |
|
1013 | 2 | unset($special[self::T_LITERAL]); |
|
1014 | |||
1015 | 2 | $this->compileOpenTag($node, $attributes, $special, $out); |
|
1016 | |||
1017 | 2 | foreach ($node->childNodes as $childNode) { |
|
1018 | 2 | $html = $childNode->ownerDocument->saveHTML($childNode); |
|
1019 | 2 | $out->echoLiteral($html); |
|
1020 | 2 | } |
|
1021 | |||
1022 | 2 | $this->compileCloseTag($node, $special, $out); |
|
1023 | 2 | } |
|
1024 | |||
1025 | 22 | protected function compileElement(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
1026 | 22 | $this->compileOpenTag($node, $attributes, $special, $out); |
|
1027 | |||
1028 | 22 | foreach ($node->childNodes as $childNode) { |
|
1029 | 22 | $this->compileNode($childNode, $out); |
|
1030 | 22 | } |
|
1031 | |||
1032 | 22 | $this->compileCloseTag($node, $special, $out); |
|
1033 | 22 | } |
|
1034 | |||
1035 | /** |
||
1036 | * Find a special node in relation to another node. |
||
1037 | * |
||
1038 | * This method is used to find things such as x-empty and x-else elements. |
||
1039 | * |
||
1040 | * @param DOMElement $node The node to search in relation to. |
||
1041 | * @param string $attribute The name of the attribute to search for. |
||
1042 | * @param string $parentAttribute The name of the parent attribute to resolve conflicts. |
||
1043 | * @return DOMElement|null Returns the found element node or **null** if not found. |
||
1044 | */ |
||
1045 | 25 | protected function findSpecialNode(DOMElement $node, $attribute, $parentAttribute) { |
|
1046 | // First look for a sibling after the node. |
||
1047 | 25 | for ($sibNode = $node->nextSibling; $sibNode !== null; $sibNode = $sibNode->nextSibling) { |
|
1048 | 2 | if ($sibNode instanceof DOMElement && $sibNode->hasAttribute($attribute)) { |
|
1049 | 2 | return $sibNode; |
|
1050 | } |
||
1051 | |||
1052 | // Stop searching if we encounter another node. |
||
1053 | 2 | if (!$this->isEmptyText($sibNode)) { |
|
1054 | break; |
||
1055 | } |
||
1056 | 2 | } |
|
1057 | |||
1058 | // Next look inside the node. |
||
1059 | 23 | $parentFound = false; |
|
1060 | 23 | foreach ($node->childNodes as $childNode) { |
|
1061 | 22 | if (!$parentFound && $childNode instanceof DOMElement && $childNode->hasAttribute($attribute)) { |
|
1062 | 2 | return $childNode; |
|
1063 | } |
||
1064 | |||
1065 | 22 | if ($childNode instanceof DOMElement) { |
|
1066 | 16 | $parentFound = $childNode->hasAttribute($parentAttribute); |
|
1067 | 22 | } elseif ($childNode instanceof \DOMText && !empty(trim($childNode->data))) { |
|
1068 | 6 | $parentFound = false; |
|
1069 | 6 | } |
|
1070 | 23 | } |
|
1071 | |||
1072 | 21 | return null; |
|
1073 | } |
||
1074 | |||
1075 | /** |
||
1076 | * @param DOMElement $node |
||
1077 | * @param array $attributes |
||
1078 | * @param array $special |
||
1079 | * @param CompilerBuffer $out |
||
1080 | */ |
||
1081 | 16 | private function compileEachLoop(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
1082 | 16 | $each = $this->expr($special[self::T_EACH]->value, $out); |
|
1083 | 16 | unset($special[self::T_EACH]); |
|
1084 | |||
1085 | 16 | $as = ['', $out->depthName('props', 1)]; |
|
1086 | 16 | $scope = ['this' => $as[1]]; |
|
1087 | 16 | if (!empty($special[self::T_AS])) { |
|
1088 | 9 | if (preg_match('`^(?:([a-z0-9]+)\s+)?([a-z0-9]+)$`i', $special[self::T_AS]->value, $m)) { |
|
1089 | 8 | $scope = [$m[2] => $as[1]]; |
|
1090 | 8 | if (!empty($m[1])) { |
|
1091 | 5 | $scope[$m[1]] = $as[0] = $out->depthName('i', 1); |
|
1092 | |||
1093 | // Add loop tracking variables. |
||
1094 | 5 | $d = $out->depthName('', 1); |
|
1095 | 5 | } |
|
1096 | 8 | } else { |
|
1097 | 1 | throw $out->createCompilerException( |
|
1098 | 1 | $special[self::T_AS], |
|
1099 | 1 | new \Exception("Invalid identifier in x-as attribute.") |
|
1100 | 1 | ); |
|
1101 | } |
||
1102 | 8 | } |
|
1103 | 15 | unset($special[self::T_AS]); |
|
1104 | |||
1105 | 15 | if (isset($d)) { |
|
1106 | 5 | $out->appendCode("\$count$d = count($each);\n"); |
|
1107 | 5 | $out->appendCode("\$index$d = -1;\n"); |
|
1108 | 5 | } |
|
1109 | |||
1110 | 15 | if (empty($as[0])) { |
|
1111 | 10 | $out->appendCode("foreach ($each as \${$as[1]}) {\n"); |
|
1112 | 10 | } else { |
|
1113 | 5 | $out->appendCode("foreach ($each as \${$as[0]} => \${$as[1]}) {\n"); |
|
1114 | } |
||
1115 | 15 | $out->depth(+1); |
|
1116 | 15 | $out->indent(+1); |
|
1117 | 15 | $out->pushScope($scope); |
|
1118 | |||
1119 | 15 | if (isset($d)) { |
|
1120 | 5 | $out->appendCode("\$index$d++;\n"); |
|
1121 | 5 | $out->appendCode("\$first$d = \$index$d === 0;\n"); |
|
1122 | 5 | $out->appendCode("\$last$d = \$index$d === \$count$d - 1;\n"); |
|
1123 | 5 | } |
|
1124 | |||
1125 | 15 | foreach ($node->childNodes as $childNode) { |
|
1126 | 15 | $this->compileNode($childNode, $out); |
|
1127 | 15 | } |
|
1128 | |||
1129 | 15 | $out->indent(-1); |
|
1130 | 15 | $out->depth(-1); |
|
1131 | 15 | $out->popScope(); |
|
1132 | 15 | $out->appendCode("}\n"); |
|
1133 | 15 | } |
|
1134 | |||
1135 | 66 | protected function ltrim($text, \DOMNode $node, CompilerBuffer $out) { |
|
1136 | 66 | if ($this->inPre($node)) { |
|
1137 | return $text; |
||
1138 | } |
||
1139 | |||
1140 | |||
1141 | 66 | for ($sib = $node->previousSibling; $sib !== null && $this->canSkip($sib, $out); $sib = $sib->previousSibling) { |
|
1142 | // |
||
1143 | 5 | } |
|
1144 | 66 | if ($sib === null) { |
|
1145 | 66 | return ltrim($text); |
|
1146 | } |
||
1147 | // if ($this->canSkip($sib, $out)) { |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
61% 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. ![]() |
|||
1148 | // return ltrim($text); |
||
1149 | // } |
||
1150 | |||
1151 | 7 | $text = preg_replace('`^\s*\n\s*`', "\n", $text, -1, $count); |
|
1152 | 7 | if ($count === 0) { |
|
1153 | $text = preg_replace('`^\s+`', ' ', $text); |
||
1154 | } |
||
1155 | |||
1156 | // if ($sib !== null && ($sib->nodeType === XML_COMMENT_NODE || in_array($sib->tagName, static::$blocks))) { |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
53% 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. ![]() |
|||
1157 | // return ltrim($text); |
||
1158 | // } |
||
1159 | 7 | return $text; |
|
1160 | } |
||
1161 | |||
1162 | /** |
||
1163 | * Whether or not a node can be skipped for the purposes of trimming whitespace. |
||
1164 | * |
||
1165 | * @param DOMNode|null $node The node to test. |
||
1166 | * @param CompilerBuffer|null $out The compiler information. |
||
1167 | * @return bool Returns **true** if whitespace can be trimmed right up to the node or **false** otherwise. |
||
1168 | */ |
||
1169 | 13 | private function canSkip(\DOMNode $node, CompilerBuffer $out) { |
|
1170 | 13 | if ($out->getNodeProp($node, 'skip')) { |
|
1171 | 2 | return true; |
|
1172 | } |
||
1173 | |||
1174 | 13 | switch ($node->nodeType) { |
|
1175 | 13 | case XML_TEXT_NODE: |
|
1176 | 2 | return false; |
|
1177 | 13 | case XML_COMMENT_NODE: |
|
1178 | 1 | return true; |
|
1179 | 13 | case XML_ELEMENT_NODE: |
|
1180 | /* @var \DOMElement $node */ |
||
1181 | 13 | if ($node->tagName === self::T_X |
|
1182 | 13 | || ($node->tagName === 'script' && $node->hasAttribute(self::T_AS)) // expression assignment |
|
1183 | 12 | || ($node->hasAttribute(self::T_WITH) && $node->hasAttribute(self::T_AS)) // with assignment |
|
1184 | 9 | || ($node->hasAttribute(self::T_BLOCK) || $node->hasAttribute(self::T_COMPONENT)) |
|
1185 | 13 | ) { |
|
1186 | 6 | return true; |
|
1187 | } |
||
1188 | 8 | } |
|
1189 | |||
1190 | 8 | return false; |
|
1191 | } |
||
1192 | |||
1193 | 66 | protected function rtrim($text, \DOMNode $node, CompilerBuffer $out) { |
|
1194 | 66 | if ($this->inPre($node)) { |
|
1195 | return $text; |
||
1196 | } |
||
1197 | |||
1198 | 66 | for ($sib = $node->nextSibling; $sib !== null && $this->canSkip($sib, $out); $sib = $sib->nextSibling) { |
|
1199 | // |
||
1200 | 5 | } |
|
1201 | 66 | if ($sib === null) { |
|
1202 | 65 | return rtrim($text); |
|
1203 | } |
||
1204 | |||
1205 | 5 | $text = preg_replace('`\s*\n\s*$`', "\n", $text, -1, $count); |
|
1206 | 5 | if ($count === 0) { |
|
1207 | 5 | $text = preg_replace('`\s+$`', ' ', $text); |
|
1208 | 5 | } |
|
1209 | |||
1210 | // if ($sib !== null && ($sib->nodeType === XML_COMMENT_NODE || in_array($sib->tagName, static::$blocks))) { |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
53% 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. ![]() |
|||
1211 | // return rtrim($text); |
||
1212 | // } |
||
1213 | 5 | return $text; |
|
1214 | } |
||
1215 | |||
1216 | 66 | protected function inPre(\DOMNode $node) { |
|
1217 | 66 | for ($node = $node->parentNode; $node !== null; $node = $node->parentNode) { |
|
1218 | 66 | if (in_array($node->nodeType, ['code', 'pre'], true)) { |
|
1219 | return true; |
||
1220 | } |
||
1221 | 66 | } |
|
1222 | 66 | return false; |
|
1223 | } |
||
1224 | |||
1225 | 4 | private function compileChildBlock(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
1226 | /* @var DOMAttr $child */ |
||
1227 | 4 | $child = $special[self::T_CHILDREN]; |
|
1228 | 4 | unset($special[self::T_CHILDREN]); |
|
1229 | |||
1230 | 4 | $name = $child->value === '' ? 0 : strtolower($child->value); |
|
1231 | 4 | if ($name !== 0) { |
|
1232 | 2 | if (empty($name)) { |
|
1233 | throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("Block names cannot be empty.")); |
||
1234 | } |
||
1235 | 2 | if (!preg_match(self::IDENT_REGEX, $name)) { |
|
1236 | throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("The block name isn't a valid identifier.")); |
||
1237 | } |
||
1238 | 2 | } |
|
1239 | |||
1240 | 4 | $keyStr = var_export($name, true); |
|
1241 | |||
1242 | 4 | $this->compileOpenTag($node, $attributes, $special, $out, true); |
|
1243 | |||
1244 | 4 | $out->appendCode("\$this->writeChildren(\$children[{$keyStr}]);\n"); |
|
1245 | |||
1246 | 4 | $this->compileCloseTag($node, $special, $out, true); |
|
1247 | 4 | } |
|
1248 | |||
1249 | /** |
||
1250 | * Compile an `<script type="ebi">` node. |
||
1251 | * |
||
1252 | * @param DOMElement $node The node to compile. |
||
1253 | * @param DOMAttr[] $attributes The node's attributes. |
||
1254 | * @param DOMAttr[] $special An array of special attributes. |
||
1255 | * @param CompilerBuffer $out The compiler output. |
||
1256 | */ |
||
1257 | 10 | private function compileExpressionNode(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) { |
|
1258 | 10 | $str = $node->nodeValue; |
|
1259 | |||
1260 | try { |
||
1261 | 10 | $expr = $this->expr($str, $out); |
|
1262 | 10 | } catch (SyntaxError $ex) { |
|
1263 | 1 | $context = []; |
|
1264 | 1 | if (preg_match('`^(.*) around position (\d*)\.$`', $ex->getMessage(), $m)) { |
|
1265 | 1 | $add = substr_count($str, "\n", 0, $m[2]); |
|
1266 | |||
1267 | 1 | $context['line'] = $node->getLineNo() + $add; |
|
1268 | 1 | } |
|
1269 | |||
1270 | 1 | throw $out->createCompilerException($node, $ex, $context); |
|
1271 | } |
||
1272 | |||
1273 | 9 | if (isset($special[self::T_AS])) { |
|
1274 | 7 | if (null !== $this->closest($node, function (\DOMNode $n) use ($out) { |
|
1275 | 7 | return $out->getNodeProp($n, self::T_INCLUDE); |
|
1276 | 7 | })) { |
|
1277 | 1 | throw $out->createCompilerException( |
|
1278 | 1 | $node, |
|
1279 | 1 | new \Exception("Expressions with x-as assignments cannot be declared inside child blocks.") |
|
1280 | 1 | ); |
|
1281 | } |
||
1282 | |||
1283 | 6 | if (preg_match(self::IDENT_REGEX, $special[self::T_AS]->value, $m)) { |
|
1284 | // The template specified an x-as attribute to alias the with expression. |
||
1285 | 5 | $out->depth(+1); |
|
1286 | 5 | $scope = [$m[1] => $out->depthName('expr')]; |
|
1287 | 5 | $out->pushScope($scope); |
|
1288 | 5 | $out->appendCode('$'.$out->depthName('expr')." = $expr;\n"); |
|
1289 | 5 | } else { |
|
1290 | 1 | throw $out->createCompilerException( |
|
1291 | 1 | $special[self::T_AS], |
|
1292 | 1 | new \Exception("Invalid identifier in x-as attribute.") |
|
1293 | 1 | ); |
|
1294 | } |
||
1295 | 7 | } elseif (!empty($special[self::T_UNESCAPE])) { |
|
1296 | 1 | $out->echoCode($expr); |
|
1297 | 1 | } else { |
|
1298 | 1 | $out->echoCode($this->compileEscape($expr)); |
|
1299 | } |
||
1300 | 7 | } |
|
1301 | |||
1302 | /** |
||
1303 | * Similar to jQuery's closest method. |
||
1304 | * |
||
1305 | * @param DOMNode $node |
||
1306 | * @param callable $test |
||
1307 | * @return DOMNode |
||
1308 | */ |
||
1309 | 7 | private function closest(\DOMNode $node, callable $test) { |
|
1310 | 7 | for ($visitor = $node; $visitor !== null && !$test($visitor); $visitor = $visitor->parentNode) { |
|
1311 | // Do nothing. The logic is all in the loop. |
||
1312 | 7 | } |
|
1313 | 7 | return $visitor; |
|
1314 | } |
||
1315 | |||
1316 | /** |
||
1317 | * @param DOMElement $node |
||
1318 | * @param $attributes |
||
1319 | * @param $special |
||
1320 | * @param CompilerBuffer $out |
||
1321 | */ |
||
1322 | 39 | protected function compileBasicElement(DOMElement $node, $attributes, $special, CompilerBuffer $out) { |
|
1323 | 39 | $this->compileOpenTag($node, $attributes, $special, $out); |
|
1324 | |||
1325 | 39 | foreach ($node->childNodes as $childNode) { |
|
1326 | 37 | $this->compileNode($childNode, $out); |
|
1327 | 39 | } |
|
1328 | |||
1329 | 38 | $this->compileCloseTag($node, $special, $out); |
|
1330 | 38 | } |
|
1331 | |||
1332 | 31 | protected function compileEscape($php) { |
|
1333 | // return 'htmlspecialchars('.$php.')'; |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
56% 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. ![]() |
|||
1334 | 31 | return '$this->escape('.$php.')'; |
|
1335 | } |
||
1336 | } |
||
1337 |
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.