Completed
Push — master ( 010cd6...22a8dd )
by Todd
11s
created

Compiler::isComponent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
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';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
17
    const T_EACH = 'x-each';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
18
    const T_WITH = 'x-with';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
19
    const T_LITERAL = 'x-literal';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
20
    const T_AS = 'x-as';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
21
    const T_COMPONENT = 'x-component';
22
    const T_CHILDREN = 'x-children';
23
    const T_BLOCK = 'x-block';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
24
    const T_ELSE = 'x-else';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
25
    const T_EMPTY = 'x-empty';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
26
    const T_X = 'x';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
27
    const T_INCLUDE = 'x-include';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
28
    const T_EBI = 'ebi';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
29
    const T_UNESCAPE = 'x-unescape';
30
    const T_TAG = 'x-tag';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
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
Unused Code Comprehensibility introduced by
50% 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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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.

Loading history...
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 77
    public function __construct() {
241 77
        $this->expressions = new ExpressionLanguage();
242 77
        $this->expressions->setNamePattern('/[@a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A');
243 77
        $this->expressions->register(
244 77
            'hasChildren',
245 77
            function ($name = null) {
246 1
                return empty($name) ? 'isset($children[0])' : "isset(\$children[$name ?: 0])";
247 77
            },
248 77
            function ($name = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
249
                return false;
250 77
            });
251 77
    }
252
253
    /**
254
     * Register a runtime function.
255
     *
256
     * @param string $name The name of the function.
257
     * @param callable $function The function callback.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $function not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
258
     */
259 75
    public function defineFunction($name, $function = null) {
260 75
        if ($function === null) {
261 1
            $function = $name;
262
        }
263
264 75
        $this->expressions->register(
265 75
            $name,
266 75
            $this->getFunctionCompiler($name, $function),
267 75
            $this->getFunctionEvaluator($function)
268
        );
269 75
    }
270
271 75
    private function getFunctionEvaluator($function) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
272 75
        if ($function === 'empty') {
273 75
            return function ($expr) {
274
                return empty($expr);
275 75
            };
276 74
        } elseif ($function === 'isset') {
277
            return function ($expr) {
278
                return isset($expr);
279
            };
280
        }
281
282 74
        return $function;
283
    }
284
285 75
    private function getFunctionCompiler($name, $function) {
286 75
        $var = var_export(strtolower($name), true);
287 75
        $fn = function (...$args) use ($var) {
288 2
            return "\$this->call($var, ".implode(', ', $args).')';
289 75
        };
290
291 75
        if (is_string($function)) {
292 75
            $fn = function (...$args) use ($function) {
293 15
                return $function.'('.implode(', ', $args).')';
294 75
            };
295 74
        } elseif (is_array($function)) {
296 74
            if (is_string($function[0])) {
297
                $fn = function (...$args) use ($function) {
298
                    return "$function[0]::$function[1](".implode(', ', $args).')';
299
                };
300 74
            } elseif ($function[0] instanceof Ebi) {
301 74
                $fn = function (...$args) use ($function) {
302 9
                    return "\$this->$function[1](".implode(', ', $args).')';
303 74
                };
304
            }
305
        }
306
307 75
        return $fn;
308
    }
309
310 69
    public function compile($src, array $options = []) {
311 69
        $options += ['basename' => '', 'path' => '', 'runtime' => true];
312
313 69
        $src = trim($src);
314
315 69
        $out = new CompilerBuffer();
316
317 69
        $out->setBasename($options['basename']);
318 69
        $out->setSource($src);
319 69
        $out->setPath($options['path']);
320
321 69
        $dom = new \DOMDocument();
322
323 69
        $fragment = false;
324 69
        if (strpos($src, '<html') === false) {
325 67
            $src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><html><body>$src</body></html>";
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
326 67
            $fragment = true;
327
        }
328
329 69
        libxml_use_internal_errors(true);
330 69
        $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.

Loading history...
332
333 69
        if ($options['runtime']) {
334 68
            $name = var_export($options['basename'], true);
335 68
            $out->appendCode("\$this->defineComponent($name, function (\$props = [], \$children = []) {\n");
336
        } else {
337 1
            $out->appendCode("function (\$props = [], \$children = []) {\n");
338
        }
339
340 69
        $out->pushScope(['this' => 'props']);
341 69
        $out->indent(+1);
342
343 69
        $parent = $fragment ? $dom->firstChild->nextSibling->firstChild : $dom;
344
345 69
        foreach ($parent->childNodes as $node) {
346 69
            $this->compileNode($node, $out);
347
        }
348
349 61
        $out->indent(-1);
350 61
        $out->popScope();
351
352 61
        if ($options['runtime']) {
353 60
            $out->appendCode("});");
354
        } else {
355 1
            $out->appendCode("};");
356
        }
357
358 61
        $r = $out->flush();
359
360 61
        $errs = libxml_get_errors();
0 ignored issues
show
Unused Code introduced by
$errs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
361
362 61
        return $r;
363
    }
364
365 52
    protected function isComponent($tag) {
366 52
        return !isset(static::$htmlTags[$tag]);
367
    }
368
369 69
    protected function compileNode(DOMNode $node, CompilerBuffer $out) {
370 69
        if ($out->getNodeProp($node, 'skip')) {
371 4
            return;
372
        }
373
374 69
        switch ($node->nodeType) {
375 69
            case XML_TEXT_NODE:
376 55
                $this->compileTextNode($node, $out);
377 55
                break;
378 64
            case XML_ELEMENT_NODE:
379
                /* @var \DOMElement $node */
380 64
                $this->compileElementNode($node, $out);
381 57
                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");
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
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".
0 ignored issues
show
Unused Code introduced by
$r is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
394
                    '// '.str_replace("\n", "\n// ", $node->ownerDocument->saveHTML($node));
395
        }
396 63
    }
397
398
    protected function domToArray(DOMNode $root) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
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
        }
449 1
    }
450
451 57
    protected function compileTextNode(DOMNode $node, CompilerBuffer $out) {
452 57
        $nodeText = $node->nodeValue;
453
454 57
        $items = $this->splitExpressions($nodeText);
455 57
        foreach ($items as $i => list($text, $offset)) {
456 57
            if (preg_match('`^{\S`', $text)) {
457 33
                if (preg_match('`^{\s*unescape\((.+)\)\s*}$`', $text, $m)) {
458 3
                    $out->echoCode($this->expr($m[1], $out));
459
                } else {
460
                    try {
461 31
                        $expr = substr($text, 1, -1);
462 31
                        $out->echoCode($this->compileEscape($this->expr($expr, $out)));
463 1
                    } catch (SyntaxError $ex) {
464 1
                        $nodeLineCount = substr_count($nodeText, "\n");
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
465 1
                        $offsetLineCount = substr_count($nodeText, "\n", 0, $offset);
466 1
                        $line = $node->getLineNo() - $nodeLineCount + $offsetLineCount;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 12 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
467 33
                        throw $out->createCompilerException($node, $ex, ['source' => $expr, 'line' => $line]);
468
                    }
469
                }
470
            } else {
471 57
                if ($i === 0) {
472 57
                    $text = $this->ltrim($text, $node, $out);
473
                }
474 57
                if ($i === count($items) - 1) {
475 57
                    $text = $this->rtrim($text, $node, $out);
476
                }
477
478 57
                $out->echoLiteral($text);
479
            }
480
        }
481 57
    }
482
483 64
    protected function compileElementNode(DOMElement $node, CompilerBuffer $out) {
484 64
        list($attributes, $special) = $this->splitAttributes($node);
485
486 64
        if ($node->tagName === 'script' && ((isset($attributes['type']) && $attributes['type']->value === self::T_EBI) || !empty($special[self::T_AS]) || !empty($special[self::T_UNESCAPE]))) {
487 9
            $this->compileExpressionNode($node, $attributes, $special, $out);
488 57
        } elseif (!empty($special) || $this->isComponent($node->tagName)) {
489 41
            $this->compileSpecialNode($node, $attributes, $special, $out);
490
        } else {
491 31
            $this->compileBasicElement($node, $attributes, $special, $out);
492
        }
493 57
    }
494
495 57
    protected function splitExpressions($value) {
496 57
        $values = preg_split('`({\S[^}]*?})`', $value, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
497 57
        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 61
    protected function expr($expr, CompilerBuffer $out, DOMAttr $attr = null) {
509 61
        $names = $out->getScopeVariables();
510
511
        try {
512 61
            $compiled = $this->expressions->compile($expr, function ($name) use ($names) {
0 ignored issues
show
Documentation introduced by
function ($name) use($na...e, true) . ']'; } } is of type object<Closure>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
513 54
                if (isset($names[$name])) {
514 33
                    return $names[$name];
515 34
                } elseif ($name[0] === '@') {
516 1
                    return 'this->meta['.var_export(substr($name, 1), true).']';
517
                } else {
518 33
                    return $names['this'].'['.var_export($name, true).']';
519
                }
520 61
            });
521 3
        } 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 58
        if ($attr !== null && null !== $fn = $this->getAttributeFunction($attr)) {
530 6
            $compiled = call_user_func($fn, $compiled);
531
        }
532
533 58
        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 32
    private function getAttributeFunction(DOMAttr $attr) {
549 32
        $keys = ['@'.$attr->ownerElement->tagName.':'.$attr->name, '@'.$attr->name];
550
551 32
        foreach ($keys as $key) {
552 32
            if (null !== $fn = $this->expressions->getFunctionCompiler($key)) {
553 32
                return $fn;
554
            }
555
        }
556 26
    }
557
558
    /**
559
     * @param DOMElement $node
560
     */
561 64
    protected function splitAttributes(DOMElement $node) {
562 64
        $attributes = [];
563 64
        $special = [];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
564
565 64
        foreach ($node->attributes as $name => $attribute) {
566 60
            if (isset(static::$special[$name])) {
567 45
                $special[$name] = $attribute;
568
            } else {
569 60
                $attributes[$name] = $attribute;
570
            }
571
        }
572
573 64
        uksort($special, function ($a, $b) {
574 13
            return strnatcmp(static::$special[$a], static::$special[$b]);
575 64
        });
576
577 64
        return [$attributes, $special];
578
    }
579
580 41
    protected function compileSpecialNode(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
581 41
        $specialName = key($special);
582
583
        switch ($specialName) {
584 41
            case self::T_COMPONENT:
585 10
                $this->compileComponentRegister($node, $attributes, $special, $out);
586 10
                break;
587 41
            case self::T_IF:
588 10
                $this->compileIf($node, $attributes, $special, $out);
589 9
                break;
590 40
            case self::T_EACH:
591 16
                $this->compileEach($node, $attributes, $special, $out);
592 15
                break;
593 28
            case self::T_BLOCK:
594 3
                $this->compileBlock($node, $attributes, $special, $out);
595 2
                break;
596 27
            case self::T_CHILDREN:
597 4
                $this->compileChildBlock($node, $attributes, $special, $out);
598 4
                break;
599 27
            case self::T_INCLUDE:
600 1
                $this->compileComponentInclude($node, $attributes, $special, $out);
601 1
                break;
602 27
            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
                } else {
607 2
                    $this->compileWith($node, $attributes, $special, $out);
608
                }
609 4
                break;
610 25
            case self::T_LITERAL:
611 2
                $this->compileLiteral($node, $attributes, $special, $out);
612 2
                break;
613 23
            case '':
614 23
                if ($this->isComponent($node->tagName)) {
615 9
                    $this->compileComponentInclude($node, $attributes, $special, $out);
616
                } else {
617 21
                    $this->compileElement($node, $attributes, $special, $out);
618
                }
619 22
                break;
620 1
            case self::T_TAG:
621
            default:
622
                // This is only a tag node so it just gets compiled as an element.
623 1
                $this->compileBasicElement($node, $attributes, $special, $out);
624 1
                break;
625
        }
626 36
    }
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
        }
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()));
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
676 2
        $vars[] = 'children';
677 2
        $use = '$'.implode(', $', array_unique($vars));
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
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
        }
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 13
    protected function compileComponentInclude(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
704
        // Mark the node as a component include.
705 13
        $out->setNodeProp($node, self::T_INCLUDE, true);
706
707
        // Generate the attributes into a property array.
708 13
        $props = [];
709 13
        foreach ($attributes as $name => $attribute) {
710
            /* @var DOMAttr $attr */
711 5
            if ($this->isExpression($attribute->value)) {
712 4
                $expr = $this->expr(substr($attribute->value, 1, -1), $out, $attribute);
713
            } else {
714 1
                $expr = var_export($attribute->value, true);
715
            }
716
717 5
            $props[] = var_export($name, true).' => '.$expr;
718
        }
719 13
        $propsStr = '['.implode(', ', $props).']';
720
721 13
        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
        } elseif (empty($props)) {
727
            // By default the current context is passed to components.
728 6
            $propsStr = $this->expr('this', $out);
729
        }
730
731
        // Compile the children blocks.
732 13
        $blocks = $this->compileComponentBlocks($node, $out);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
733 12
        $blocksStr = $blocks->flush();
734
735 12
        if (isset($special[self::T_INCLUDE])) {
736 1
            $name = $this->expr($special[self::T_INCLUDE]->value, $out, $special[self::T_INCLUDE]);
737
        } else {
738 11
            $name = var_export($node->tagName, true);
739
        }
740
741 12
        $out->appendCode("\$this->write($name, $propsStr, $blocksStr);\n");
742 12
    }
743
744
    /**
745
     * @param DOMElement $parent
746
     * @return CompilerBuffer
747
     */
748 13
    protected function compileComponentBlocks(DOMElement $parent, CompilerBuffer $out) {
749 13
        $blocksOut = new CompilerBuffer(CompilerBuffer::STYLE_ARRAY, [
750 13
            'baseIndent' => $out->getIndent(),
751 13
            'indent' => $out->getIndent() + 1,
752 13
            'depth' => $out->getDepth(),
753 13
            'scopes' => $out->getAllScopes(),
754 13
            'nodeProps' => $out->getNodePropArray()
755
        ]);
756 13
        $blocksOut->setSource($out->getSource());
757
758 13
        if ($this->isEmptyNode($parent)) {
759 9
            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
            }
771 4
        } finally {
772 5
            $blocksOut->indent(-1);
773 5
            $blocksOut->appendCode("}");
774
        }
775
776 4
        return $blocksOut;
777
    }
778
779 29
    protected function compileTagComment(DOMElement $node, $attributes, $special, CompilerBuffer $out) {
0 ignored issues
show
Unused Code introduced by
The parameter $attributes is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
780
        // Don't double up comments.
781 29
        if ($node->previousSibling && $node->previousSibling->nodeType === XML_COMMENT_NODE) {
782
            return;
783
        }
784
785 29
        $str = '<'.$node->tagName;
786 29
        foreach ($special as $attr) {
787
            /* @var DOMAttr $attr */
788 29
            $str .= ' '.$attr->name.(empty($attr->value) ? '' : '="'.htmlspecialchars($attr->value).'"');
789
        }
790 29
        $str .= '>';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
791 29
        $comments = explode("\n", $str);
792 29
        foreach ($comments as $comment) {
793 29
            $out->appendCode("// $comment\n");
794
        }
795 29
    }
796
797
    /**
798
     * @param DOMElement $node
799
     * @param DOMAttr[] $attributes
800
     * @param DOMAttr[] $special
801
     * @param CompilerBuffer $out
802
     * @param bool $force
803
     */
804 53
    protected function compileOpenTag(DOMElement $node, $attributes, $special, CompilerBuffer $out, $force = false) {
805 53
        $tagNameExpr = !empty($special[self::T_TAG]) ? $special[self::T_TAG]->value : '';
806
807 53
        if ($node->tagName === self::T_X && empty($tagNameExpr)) {
808 5
            return;
809
        }
810
811 51
        if (!empty($tagNameExpr)) {
812 1
            $tagNameExpr = $this->expr($tagNameExpr, $out, $special[self::T_TAG]);
813 1
            $out->echoLiteral('<');
814 1
            $out->echoCode($tagNameExpr);
815
        } else {
816 51
            $out->echoLiteral('<'.$node->tagName);
817
        }
818
819
        /* @var DOMAttr $attribute */
820 51
        foreach ($attributes as $name => $attribute) {
821
            // Check for an attribute expression.
822 19
            if ($this->isExpression($attribute->value)) {
823 15
                $out->echoCode(
824 15
                    '$this->attribute('.var_export($name, true).', '.
825 15
                    $this->expr(substr($attribute->value, 1, -1), $out, $attribute).
826 15
                    ')');
827 5
            } elseif (null !== $fn = $this->getAttributeFunction($attribute)) {
828 3
                $value  = call_user_func($fn, var_export($attribute->value, true));
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
829
830 3
                $out->echoCode('$this->attribute('.var_export($name, true).', '.$value.')');
831 4
            } elseif ((empty($attribute->value) || $attribute->value === $name) && isset(self::$boolAttributes[$name])) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $attribute->value (string) and $name (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
832 2
                $out->echoLiteral(' '.$name);
833
            } else {
834 4
                $out->echoLiteral(' '.$name.'="');
835 4
                $out->echoLiteral(htmlspecialchars($attribute->value));
836 19
                $out->echoLiteral('"');
837
            }
838
        }
839
840 51
        if ($node->hasChildNodes() || $force) {
841 49
            $out->echoLiteral('>');
842
        } else {
843 3
            $out->echoLiteral(" />");
844
        }
845 51
    }
846
847 22
    private function isExpression($value) {
848 22
        return preg_match('`^{\S.*}$`', $value);
849
    }
850
851 51
    protected function compileCloseTag(DOMElement $node, $special, CompilerBuffer $out, $force = false) {
852 51
        if (!$force && !$node->hasChildNodes()) {
853 3
            return;
854
        }
855
856 49
        $tagNameExpr = !empty($special[self::T_TAG]) ? $special[self::T_TAG]->value : '';
857 49
        if (!empty($tagNameExpr)) {
858 1
            $tagNameExpr = $this->expr($tagNameExpr, $out, $special[self::T_TAG]);
859 1
            $out->echoLiteral('</');
860 1
            $out->echoCode($tagNameExpr);
861 1
            $out->echoLiteral('>');
862 49
        } elseif ($node->tagName !== self::T_X) {
863 47
            $out->echoLiteral("</{$node->tagName}>");
864
        }
865 49
    }
866
867 4
    protected function isEmptyText(DOMNode $node) {
868 4
        return $node instanceof \DOMText && empty(trim($node->data));
869
    }
870
871 13
    protected function isEmptyNode(DOMNode $node) {
872 13
        if (!$node->hasChildNodes()) {
873 9
            return true;
874
        }
875
876 5
        foreach ($node->childNodes as $childNode) {
877 5
            if ($childNode instanceof DOMElement) {
878 3
                return false;
879
            }
880 2
            if ($childNode instanceof \DOMText && !$this->isEmptyText($childNode)) {
881 2
                return false;
882
            }
883
        }
884
885
        return true;
886
    }
887
888 10
    protected function compileIf(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
889 10
        $this->compileTagComment($node, $attributes, $special, $out);
890 10
        $expr = $this->expr($special[self::T_IF]->value, $out, $special[self::T_IF]);
891 9
        unset($special[self::T_IF]);
892
893 9
        $elseNode = $this->findSpecialNode($node, self::T_ELSE, self::T_IF);
894 9
        $out->setNodeProp($elseNode, 'skip', true);
895
896 9
        $out->appendCode('if ('.$expr.") {\n");
897 9
        $out->indent(+1);
898
899 9
        $this->compileSpecialNode($node, $attributes, $special, $out);
900
901 9
        $out->indent(-1);
902
903 9
        if ($elseNode) {
904 2
            list($attributes, $special) = $this->splitAttributes($elseNode);
905 2
            unset($special[self::T_ELSE]);
906
907 2
            $out->appendCode("} else {\n");
908
909 2
            $out->indent(+1);
910 2
            $this->compileSpecialNode($elseNode, $attributes, $special, $out);
911 2
            $out->indent(-1);
912
        }
913
914 9
        $out->appendCode("}\n");
915 9
    }
916
917 16
    protected function compileEach(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
918 16
        $this->compileTagComment($node, $attributes, $special, $out);
919 16
        $this->compileOpenTag($node, $attributes, $special, $out);
920
921 16
        $emptyNode = $this->findSpecialNode($node, self::T_EMPTY, self::T_ELSE);
922 16
        $out->setNodeProp($emptyNode, 'skip', true);
923
924 16
        if ($emptyNode === null) {
925 14
            $this->compileEachLoop($node, $attributes, $special, $out);
926
        } else {
927 2
            $expr = $this->expr("empty({$special[self::T_EACH]->value})", $out);
928
929 2
            list ($emptyAttributes, $emptySpecial) = $this->splitAttributes($emptyNode);
930 2
            unset($emptySpecial[self::T_EMPTY]);
931
932 2
            $out->appendCode('if ('.$expr.") {\n");
933
934 2
            $out->indent(+1);
935 2
            $this->compileSpecialNode($emptyNode, $emptyAttributes, $emptySpecial, $out);
936 2
            $out->indent(-1);
937
938 2
            $out->appendCode("} else {\n");
939
940 2
            $out->indent(+1);
941 2
            $this->compileEachLoop($node, $attributes, $special, $out);
942 2
            $out->indent(-1);
943
944 2
            $out->appendCode("}\n");
945
        }
946
947 15
        $this->compileCloseTag($node, $special, $out);
948 15
    }
949
950 2
    protected function compileWith(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
951 2
        $this->compileTagComment($node, $attributes, $special, $out);
952
953 2
        $out->depth(+1);
954 2
        $scope = ['this' => $out->depthName('props')];
955 2
        if (!empty($special[self::T_AS])) {
956 2
            if (preg_match(self::IDENT_REGEX, $special[self::T_AS]->value, $m)) {
957
                // The template specified an x-as attribute to alias the with expression.
958 1
                $scope = [$m[1] => $out->depthName('props')];
959
            } else {
960 1
                throw $out->createCompilerException(
961 1
                    $special[self::T_AS],
962 1
                    new \Exception("Invalid identifier in x-as attribute.")
963
                );
964
            }
965
        }
966 1
        $with = $this->expr($special[self::T_WITH]->value, $out);
967
968 1
        unset($special[self::T_WITH], $special[self::T_AS]);
969
970 1
        $out->pushScope($scope);
971 1
        $out->appendCode('$'.$out->depthName('props')." = $with;\n");
972
973 1
        $this->compileSpecialNode($node, $attributes, $special, $out);
974
975 1
        $out->depth(-1);
976 1
        $out->popScope();
977 1
    }
978
979 2
    protected function compileLiteral(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
980 2
        $this->compileTagComment($node, $attributes, $special, $out);
981 2
        unset($special[self::T_LITERAL]);
982
983 2
        $this->compileOpenTag($node, $attributes, $special, $out);
984
985 2
        foreach ($node->childNodes as $childNode) {
986 2
            $html = $childNode->ownerDocument->saveHTML($childNode);
987 2
            $out->echoLiteral($html);
988
        }
989
990 2
        $this->compileCloseTag($node, $special, $out);
991 2
    }
992
993 21
    protected function compileElement(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
994 21
        $this->compileOpenTag($node, $attributes, $special, $out);
995
996 21
        foreach ($node->childNodes as $childNode) {
997 21
            $this->compileNode($childNode, $out);
998
        }
999
1000 21
        $this->compileCloseTag($node, $special, $out);
1001 21
    }
1002
1003
    /**
1004
     * Find a special node in relation to another node.
1005
     *
1006
     * This method is used to find things such as x-empty and x-else elements.
1007
     *
1008
     * @param DOMElement $node The node to search in relation to.
1009
     * @param string $attribute The name of the attribute to search for.
1010
     * @param string $parentAttribute The name of the parent attribute to resolve conflicts.
1011
     * @return DOMElement|null Returns the found element node or **null** if not found.
1012
     */
1013 24
    protected function findSpecialNode(DOMElement $node, $attribute, $parentAttribute) {
1014
        // First look for a sibling after the node.
1015 24
        for ($sibNode = $node->nextSibling; $sibNode !== null; $sibNode = $sibNode->nextSibling) {
1016 2
            if ($sibNode instanceof DOMElement && $sibNode->hasAttribute($attribute)) {
1017 2
                return $sibNode;
1018
            }
1019
1020
            // Stop searching if we encounter another node.
1021 2
            if (!$this->isEmptyText($sibNode)) {
1022
                break;
1023
            }
1024
        }
1025
1026
        // Next look inside the node.
1027 22
        $parentFound = false;
1028 22
        foreach ($node->childNodes as $childNode) {
1029 21
            if (!$parentFound && $childNode instanceof DOMElement && $childNode->hasAttribute($attribute)) {
1030 2
                return $childNode;
1031
            }
1032
1033 21
            if ($childNode instanceof DOMElement) {
1034 15
                $parentFound = $childNode->hasAttribute($parentAttribute);
1035 10
            } elseif ($childNode instanceof \DOMText && !empty(trim($childNode->data))) {
1036 21
                $parentFound = false;
1037
            }
1038
        }
1039
1040 20
        return null;
1041
    }
1042
1043
    /**
1044
     * @param DOMElement $node
1045
     * @param array $attributes
1046
     * @param array $special
1047
     * @param CompilerBuffer $out
1048
     */
1049 16
    private function compileEachLoop(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
0 ignored issues
show
Unused Code introduced by
The parameter $attributes is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1050 16
        $each = $this->expr($special[self::T_EACH]->value, $out);
1051 16
        unset($special[self::T_EACH]);
1052
1053 16
        $as = ['', $out->depthName('props', 1)];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
1054 16
        $scope = ['this' => $as[1]];
1055 16
        if (!empty($special[self::T_AS])) {
1056 9
            if (preg_match('`^(?:([a-z0-9]+)\s+)?([a-z0-9]+)$`i', $special[self::T_AS]->value, $m)) {
1057 8
                $scope = [$m[2] => $as[1]];
1058 8
                if (!empty($m[1])) {
1059 5
                    $scope[$m[1]] = $as[0] = $out->depthName('i', 1);
1060
1061
                    // Add loop tracking variables.
1062 8
                    $d = $out->depthName('', 1);
1063
                }
1064
            } else {
1065 1
                throw $out->createCompilerException(
1066 1
                    $special[self::T_AS],
1067 1
                    new \Exception("Invalid identifier in x-as attribute.")
1068
                );
1069
            }
1070
        }
1071 15
        unset($special[self::T_AS]);
1072
1073 15
        if (isset($d)) {
1074 5
            $out->appendCode("\$count$d = count($each);\n");
1075 5
            $out->appendCode("\$index$d = -1;\n");
1076
        }
1077
1078 15
        if (empty($as[0])) {
1079 10
            $out->appendCode("foreach ($each as \${$as[1]}) {\n");
1080
        } else {
1081 5
            $out->appendCode("foreach ($each as \${$as[0]} => \${$as[1]}) {\n");
1082
        }
1083 15
        $out->depth(+1);
1084 15
        $out->indent(+1);
1085 15
        $out->pushScope($scope);
1086
1087 15
        if (isset($d)) {
1088 5
            $out->appendCode("\$index$d++;\n");
1089 5
            $out->appendCode("\$first$d = \$index$d === 0;\n");
1090 5
            $out->appendCode("\$last$d = \$index$d === \$count$d - 1;\n");
1091
        }
1092
1093 15
        foreach ($node->childNodes as $childNode) {
1094 15
            $this->compileNode($childNode, $out);
1095
        }
1096
1097 15
        $out->indent(-1);
1098 15
        $out->depth(-1);
1099 15
        $out->popScope();
1100 15
        $out->appendCode("}\n");
1101 15
    }
1102
1103 57
    protected function ltrim($text, \DOMNode $node, CompilerBuffer $out) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1104 57
        if ($this->inPre($node)) {
1105
            return $text;
1106
        }
1107
1108
1109 57
        for ($sib = $node->previousSibling; $sib !== null && $this->canSkip($sib, $out); $sib = $sib->previousSibling) {
0 ignored issues
show
Unused Code introduced by
This for loop is empty and can be removed.

This check looks for for loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
1110
            //
1111
        }
1112 57
        if ($sib === null) {
1113 57
            return ltrim($text);
1114
        }
1115
//        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.

Loading history...
1116
//            return ltrim($text);
1117
//        }
1118
1119 7
        $text = preg_replace('`^\s*\n\s*`', "\n", $text, -1, $count);
1120 7
        if ($count === 0) {
1121
            $text = preg_replace('`^\s+`', ' ', $text);
1122
        }
1123
1124
//        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.

Loading history...
1125
//            return ltrim($text);
1126
//        }
1127 7
        return $text;
1128
    }
1129
1130
    /**
1131
     * Whether or not a node can be skipped for the purposes of trimming whitespace.
1132
     *
1133
     * @param DOMNode|null $node The node to test.
1134
     * @param CompilerBuffer|null $out The compiler information.
1135
     * @return bool Returns **true** if whitespace can be trimmed right up to the node or **false** otherwise.
1136
     */
1137 13
    private function canSkip(\DOMNode $node, CompilerBuffer $out) {
1138 13
        if ($out->getNodeProp($node, 'skip')) {
1139 2
            return true;
1140
        }
1141
1142 13
        switch ($node->nodeType) {
1143 13
            case XML_TEXT_NODE:
1144 2
                return false;
1145 13
            case XML_COMMENT_NODE:
1146 1
                return true;
1147 13
            case XML_ELEMENT_NODE:
1148
                /* @var \DOMElement $node */
1149 13
                if ($node->tagName === self::T_X
1150 12
                    || ($node->tagName === 'script' && $node->hasAttribute(self::T_AS)) // expression assignment
1151 9
                    || ($node->hasAttribute(self::T_WITH) && $node->hasAttribute(self::T_AS)) // with assignment
1152 13
                    || ($node->hasAttribute(self::T_BLOCK) || $node->hasAttribute(self::T_COMPONENT))
1153
                ) {
1154 6
                    return true;
1155
                }
1156
        }
1157
1158 8
        return false;
1159
    }
1160
1161 57
    protected function rtrim($text, \DOMNode $node, CompilerBuffer $out) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1162 57
        if ($this->inPre($node)) {
1163
            return $text;
1164
        }
1165
1166 57
        for ($sib = $node->nextSibling; $sib !== null && $this->canSkip($sib, $out); $sib = $sib->nextSibling) {
0 ignored issues
show
Unused Code introduced by
This for loop is empty and can be removed.

This check looks for for loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
1167
            //
1168
        }
1169 57
        if ($sib === null) {
1170 56
            return rtrim($text);
1171
        }
1172
1173 5
        $text = preg_replace('`\s*\n\s*$`', "\n", $text, -1, $count);
1174 5
        if ($count === 0) {
1175 5
            $text = preg_replace('`\s+$`', ' ', $text);
1176
        }
1177
1178
//        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.

Loading history...
1179
//            return rtrim($text);
1180
//        }
1181 5
        return $text;
1182
    }
1183
1184 57
    protected function inPre(\DOMNode $node) {
1185 57
        for ($node = $node->parentNode; $node !== null; $node = $node->parentNode) {
1186 57
            if (in_array($node->nodeType, ['code', 'pre'], true)) {
1187
                return true;
1188
            }
1189
        }
1190 57
        return false;
1191
    }
1192
1193 4
    private function compileChildBlock(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
1194
        /* @var DOMAttr $child */
1195 4
        $child = $special[self::T_CHILDREN];
1196 4
        unset($special[self::T_CHILDREN]);
1197
1198 4
        $name = $child->value === '' ? 0 : strtolower($child->value);
1199 4
        if ($name !== 0) {
1200 2
            if (empty($name)) {
1201
                throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("Block names cannot be empty."));
1202
            }
1203 2
            if (!preg_match(self::IDENT_REGEX, $name)) {
1204
                throw $out->createCompilerException($special[self::T_BLOCK], new \Exception("The block name isn't a valid identifier."));
1205
            }
1206
        }
1207
1208 4
        $keyStr = var_export($name, true);
1209
1210 4
        $this->compileOpenTag($node, $attributes, $special, $out, true);
1211
1212 4
        $out->appendCode("\$this->writeChildren(\$children[{$keyStr}]);\n");
1213
1214 4
        $this->compileCloseTag($node, $special, $out, true);
1215 4
    }
1216
1217
    /**
1218
     * Compile an `<script type="ebi">` node.
1219
     *
1220
     * @param DOMElement $node The node to compile.
1221
     * @param DOMAttr[] $attributes The node's attributes.
1222
     * @param DOMAttr[] $special An array of special attributes.
1223
     * @param CompilerBuffer $out The compiler output.
1224
     */
1225 9
    private function compileExpressionNode(DOMElement $node, array $attributes, array $special, CompilerBuffer $out) {
0 ignored issues
show
Unused Code introduced by
The parameter $attributes is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1226 9
        $str = $node->nodeValue;
1227
1228
        try {
1229 9
            $expr = $this->expr($str, $out);
1230 1
        } catch (SyntaxError $ex) {
1231 1
            $context = [];
1232 1
            if (preg_match('`^(.*) around position (\d*)\.$`', $ex->getMessage(), $m)) {
1233 1
                $add = substr_count($str, "\n", 0, $m[2]);
1234
1235 1
                $context['line'] = $node->getLineNo() + $add;
1236
            }
1237
1238 1
            throw $out->createCompilerException($node, $ex, $context);
1239
        }
1240
1241 8
        if (isset($special[self::T_AS])) {
1242 6
            if (null !== $this->closest($node, function (\DOMNode $n) use ($out) {
1243 6
                return $out->getNodeProp($n, self::T_INCLUDE);
1244 6
            })) {
1245 1
                throw $out->createCompilerException(
1246 1
                    $node,
1247 1
                    new \Exception("Expressions with x-as assignments cannot be declared inside child blocks.")
1248
                );
1249
            }
1250
1251 5
            if (preg_match(self::IDENT_REGEX, $special[self::T_AS]->value, $m)) {
1252
                // The template specified an x-as attribute to alias the with expression.
1253 4
                $out->depth(+1);
1254 4
                $scope = [$m[1] => $out->depthName('expr')];
1255 4
                $out->pushScope($scope);
1256 4
                $out->appendCode('$'.$out->depthName('expr')." = $expr;\n");
1257
            } else {
1258 1
                throw $out->createCompilerException(
1259 1
                    $special[self::T_AS],
1260 5
                    new \Exception("Invalid identifier in x-as attribute.")
1261
                );
1262
            }
1263 2
        } elseif (!empty($special[self::T_UNESCAPE])) {
1264 1
            $out->echoCode($expr);
1265
        } else {
1266 1
            $out->echoCode($this->compileEscape($expr));
1267
        }
1268 6
    }
1269
1270
    /**
1271
     * Similar to jQuery's closest method.
1272
     *
1273
     * @param DOMNode $node
1274
     * @param callable $test
1275
     * @return DOMNode
1276
     */
1277 6
    private function closest(\DOMNode $node, callable $test) {
1278 6
        for ($visitor = $node; $visitor !== null && !$test($visitor); $visitor = $visitor->parentNode) {
0 ignored issues
show
Unused Code introduced by
This for loop is empty and can be removed.

This check looks for for loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
1279
            // Do nothing. The logic is all in the loop.
1280
        }
1281 6
        return $visitor;
1282
    }
1283
1284
    /**
1285
     * @param DOMElement $node
1286
     * @param $attributes
1287
     * @param $special
1288
     * @param CompilerBuffer $out
1289
     */
1290 31
    protected function compileBasicElement(DOMElement $node, $attributes, $special, CompilerBuffer $out) {
1291 31
        $this->compileOpenTag($node, $attributes, $special, $out);
1292
1293 31
        foreach ($node->childNodes as $childNode) {
1294 29
            $this->compileNode($childNode, $out);
1295
        }
1296
1297 30
        $this->compileCloseTag($node, $special, $out);
1298 30
    }
1299
1300 31
    protected function compileEscape($php) {
1301
//        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.

Loading history...
1302 31
        return '$this->escape('.$php.')';
1303
    }
1304
}
1305