Issues (102)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Compiler.php (66 issues)

Upgrade to new PHP Analysis Engine

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';
0 ignored issues
show
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
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
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
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
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
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
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
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
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
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
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
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 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) {
0 ignored issues
show
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 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.
0 ignored issues
show
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 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) {
0 ignored issues
show
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 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>";
0 ignored issues
show
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 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.

Loading history...
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();
0 ignored issues
show
$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 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");
0 ignored issues
show
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
$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 72
        }
396 72
    }
397
398
    protected function domToArray(DOMNode $root) {
0 ignored issues
show
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 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");
0 ignored issues
show
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
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 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) {
0 ignored issues
show
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 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 = [];
0 ignored issues
show
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 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()));
0 ignored issues
show
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
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 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);
0 ignored issues
show
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 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) {
0 ignored issues
show
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...
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('"', '&quot;', $attr->value).'"');
797 34
        }
798 34
        $str .= '>';
0 ignored issues
show
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...
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);
0 ignored issues
show
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...
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));
0 ignored issues
show
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...
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])) {
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...
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) {
0 ignored issues
show
The parameter $special 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...
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.

Loading history...
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) {
0 ignored issues
show
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...
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)];
0 ignored issues
show
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...
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) {
0 ignored issues
show
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...
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) {
0 ignored issues
show
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...
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.

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

Loading history...
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) {
0 ignored issues
show
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...
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) {
0 ignored issues
show
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...
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.

Loading history...
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) {
0 ignored issues
show
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...
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) {
0 ignored issues
show
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...
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.

Loading history...
1334 31
        return '$this->escape('.$php.')';
1335
    }
1336
}
1337