GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — v0.89-develop ( 9799ea...0ad74a )
by Zordius
03:45
created

Parser   D

Complexity

Total Complexity 84

Size/Duplication

Total Lines 398
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 98.73%

Importance

Changes 20
Bugs 6 Features 2
Metric Value
wmc 84
c 20
b 6
f 2
lcom 1
cbo 2
dl 0
loc 398
ccs 156
cts 158
cp 0.9873
rs 4.8718

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getBlockParams() 0 7 2
A getLiteral() 0 3 3
A isSubExp() 0 3 4
A isBlockParam() 0 3 4
C getExpression() 0 62 17
B parse() 0 14 9
A subexpression() 0 11 4
C advancedVariable() 0 64 19
D analyze() 0 101 22

How to fix   Complexity   

Complex Class

Complex classes like Parser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Parser, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2015 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file to keep LightnCandy Parser
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
use \LightnCandy\Token;
24
use \LightnCandy\SafeString;
25
26
/**
27
 * LightnCandy Parser
28
 */
29
class Parser extends Token
30
{
31
    // Compile time error handling flags
32
    const BLOCKPARAM = 9999;
33
34
    /**
35
     * Get block params and fix the variable list
36
     *
37
     * @param array<boolean|integer|array> $vars parsed token
38
     *
39
     * @return array<string>|null Return list of block params or null
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
40
     *
41
     */
42 168
    public static function getBlockParams(&$vars) {
43 168
        if (isset($vars[static::BLOCKPARAM])) {
44 5
            $list = $vars[static::BLOCKPARAM];
45 5
            unset($vars[static::BLOCKPARAM]);
46 5
            return $list;
47
        }
48 164
    }
49
50
    /**
51
     * Return array presentation for an expression
52
     *
53
     * @param string $name variable name.
54
     * @param boolean $asis keep the name as is or not
55
     * @param boolean $quote add single quote or not
56
     *
57
     * @return array<integer|string> Return variable name array
58
     *
59
     */
60 80
    protected static function getLiteral($name, $asis, $quote = false) {
61 80
        return $asis ? array($name) : array(0, $quote ? "'$name'" : $name);
62
    }
63
64
    /**
65
     * Return array presentation for an expression
66
     *
67
     * @param string $v analyzed expression names.
68
     * @param array<string,array|string|integer> $context Current compile content.
69
     * @param boolean $asis keep the reference name
70
     *
71
     * @return array<integer,string> Return variable name array
72
     *
73
     * @expect array('this') when input 'this', array('flags' => array('advar' => 0, 'this' => 0)), false
74
     * @expect array() when input 'this', array('flags' => array('advar' => 0, 'this' => 1)), false
75
     * @expect array(1) when input '../', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
76
     * @expect array(1) when input '../.', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
77
     * @expect array(1) when input '../this', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
78
     * @expect array(1, 'a') when input '../a', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
79
     * @expect array(2, 'a', 'b') when input '../../a.b', array('flags' => array('advar' => 0, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
80
     * @expect array(2, '[a]', 'b') when input '../../[a].b', array('flags' => array('advar' => 0, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
81
     * @expect array(2, 'a', 'b') when input '../../[a].b', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
82
     * @expect array('id') when input 'this.id', array('flags' => array('advar' => 1, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
83
     * @expect array(0, '\'a.b\'') when input '"a.b"', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
84
     * @expect array(0, '123') when input '123', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
85
     * @expect array(0, 'null') when input 'null', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)), false
86
     */
87 583
    protected static function getExpression($v, &$context, $asis) {
88
        // handle number
89 583
        if (is_numeric($v)) {
90 24
            return static::getLiteral(strval(1 * $v), $asis);
91
        }
92
93
        // handle double quoted string
94 583
        if (preg_match('/^"(.*)"$/', $v, $matched)) {
95 44
            return static::getLiteral(preg_replace('/([^\\\\])\\\\\\\\"/', '$1"', preg_replace('/^\\\\\\\\"/', '"', $matched[1])), $asis, true);
96
        }
97
98
        // handle single quoted string
99 577
        if (preg_match('/^\\\\\'(.*)\\\\\'$/', $v, $matched)) {
100 16
            return static::getLiteral($matched[1], $asis, true);
101
        }
102
103
        // handle boolean, null and undefined
104 574
        if (preg_match('/^(true|false|null|undefined)$/', $v)) {
105 22
            return static::getLiteral(($v === 'undefined') ? 'null' : $v, $asis);
106
        }
107
108 571
        $ret = array();
109 571
        $levels = 0;
110
111
        // handle ..
112 571
        if ($v === '..') {
113 6
            $v = '../';
114
        }
115
116
        // Trace to parent for ../ N times
117 571
        $v = preg_replace_callback('/\\.\\.\\//', function() use (&$levels) {
118 40
            $levels++;
119 40
            return '';
120 571
        }, trim($v));
121
122
        // remove ./ in path
123 571
        $v = preg_replace('/\\.\\//', '', $v);
124
125 571
        if ($levels) {
126 40
            $ret[] = $levels;
127 40
            if (!$context['flags']['parent']) {
128 4
                $context['error'][] = 'Do not support {{../var}}, you should do compile with LightnCandy::FLAG_PARENT flag';
129
            }
130 40
            $context['usedFeature']['parent'] ++;
131
        }
132
133 571
        if ($context['flags']['advar'] && preg_match('/\\]/', $v)) {
134 31
            preg_match_all(static::VARNAME_SEARCH, $v, $matchedall);
135
        } else {
136 546
            preg_match_all('/([^\\.\\/]+)/', $v, $matchedall);
137
        }
138
139 571
        foreach ($matchedall[1] as $m) {
140 564
            if ($context['flags']['advar'] && substr($m, 0, 1) === '[') {
141 25
                $ret[] = substr($m, 1, -1);
142 557
            } else if ((!$context['flags']['this'] || ($m !== 'this')) && ($m !== '.')) {
143 564
                $ret[] = $m;
144
            }
145
        }
146
147 571
        return $ret;
148
    }
149
150
    /**
151
     * Parse the token and return parsed result.
152
     *
153
     * @param array<string> $token preg_match results
154
     * @param array<string,array|string|integer> $context current compile context
155
     *
156
     * @return array<boolean|integer|array> Return parsed result
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<boolean|array<boolean|integer|array>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
157
     *
158
     * @expect array(false, array(array())) when input array(0,0,0,0,0,0,0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
159
     * @expect array(true, array(array())) when input array(0,0,0,'{{',0,'{',0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
160
     * @expect array(true, array(array())) when input array(0,0,0,0,0,0,0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 1), 'rawblock' => false)
161
     * @expect array(false, array(array('a'))) when input array(0,0,0,0,0,0,0,'a'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
162
     * @expect array(false, array(array('a'), array('b'))) when input array(0,0,0,0,0,0,0,'a  b'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
163
     * @expect array(false, array(array('a'), array('"b'), array('c"'))) when input array(0,0,0,0,0,0,0,'a "b c"'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
164
     * @expect array(false, array(array('a'), array(0, '\'b c\''))) when input array(0,0,0,0,0,0,0,'a "b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
165
     * @expect array(false, array(array('a'), array('[b'), array('c]'))) when input array(0,0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
166
     * @expect array(false, array(array('a'), array('[b'), array('c]'))) when input array(0,0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
167
     * @expect array(false, array(array('a'), array('b c'))) when input array(0,0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
168
     * @expect array(false, array(array('a'), array('b c'))) when input array(0,0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
169
     * @expect array(false, array(array('a'), 'q' => array('b c'))) when input array(0,0,0,0,0,0,0,'a q=[b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
170
     * @expect array(false, array(array('a'), array('q=[b c'))) when input array(0,0,0,0,0,0,0,'a [q=[b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
171
     * @expect array(false, array(array('a'), 'q' => array('[b'), array('c]'))) when input array(0,0,0,0,0,0,0,'a q=[b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
172
     * @expect array(false, array(array('a'), 'q' => array('b'), array('c'))) when input array(0,0,0,0,0,0,0,'a [q]=b c'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
173
     * @expect array(false, array(array('a'), 'q' => array(0, '\'b c\''))) when input array(0,0,0,0,0,0,0,'a q="b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
174
     * @expect array(false, array(array(-1, array(array('foo'), array('bar')), '(foo bar)'))) when input array(0,0,0,0,0,0,0,'(foo bar)'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0, 'exhlp' => 1, 'lambda' => 0), 'ops' => array('seperator' => ''), 'usedFeature' => array('subexp' => 0), 'rawblock' => false)
175
     * @expect array(false, array(array('foo'), array("'=='"), array('bar'))) when input array(0,0,0,0,0,0,0,"foo '==' bar"), array('flags' => array('advar' => 1, 'namev' => 1, 'noesc' => 0, 'this' => 0), 'rawblock' => false)
176
     * @expect array(false, array(array(-1, array(array('foo'), array('bar')), '( foo bar)'))) when input array(0,0,0,0,0,0,0,'( foo bar)'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0, 'exhlp' => 1, 'lambda' => 0), 'ops' => array('seperator' => ''), 'usedFeature' => array('subexp' => 0), 'rawblock' => false)
177
     * @expect array(false, array(array('a'), array(0, '\' b c\''))) when input array(0,0,0,0,0,0,0,'a " b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 0, 'noesc' => 0), 'rawblock' => false)
178
     * @expect array(false, array(array('a'), 'q' => array(0, '\' b c\''))) when input array(0,0,0,0,0,0,0,'a q=" b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
179
     * @expect array(false, array(array('foo'), array(0, "' =='"), array('bar'))) when input array(0,0,0,0,0,0,0,"foo \' ==\' bar"), array('flags' => array('advar' => 1, 'namev' => 1, 'noesc' => 0, 'this' => 0), 'rawblock' => false)
180
     * @expect array(false, array(array('a'), array(' b c'))) when input array(0,0,0,0,0,0,0,'a [ b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
181
     * @expect array(false, array(array('a'), 'q' => array(0, "' d e'"))) when input array(0,0,0,0,0,0,0,"a q=\' d e\'"), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0), 'rawblock' => false)
182
     * @expect array(false, array('q' => array(-1, array(array('foo'), array('bar')), '( foo bar)'))) when input array(0,0,0,0,0,0,0,'q=( foo bar)'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0, 'exhlp' => 0, 'lambda' => 0), 'usedFeature' => array('subexp' => 0), 'ops' => array('seperator' => 0), 'rawblock' => false)
183
     */
184 589
    public static function parse(&$token, &$context) {
185 589
        $vars = static::analyze($token[static::POS_INNERTAG], $context);
186 589
        if ($token[static::POS_OP] === '>' && isset($vars[0])) {
187 69
            $fn = $vars[0];
188
        }
189
190 589
        $avars = static::advancedVariable($vars, $context, static::toString($token));
191
192 589
        if ($token[static::POS_OP] === '>' && isset($fn)) {
193 69
            $avars[0] = preg_match(SafeString::IS_SUBEXP_SEARCH, $fn) ? $avars[0] : array(preg_replace('/^("(.+)")|(\\[(.+)\\])$/', '$2$4', $fn));
194
        }
195
196 589
        return array(($token[static::POS_BEGINRAW] === '{') || ($token[static::POS_OP] === '&') || $context['flags']['noesc'] || $context['rawblock'], $avars);
197
    }
198
199
    /**
200
     * Parse a subexpression then return parsed result.
201
     *
202
     * @param string $expression the full string of a sub expression
203
     * @param array<string,array|string|integer> $context current compile context
204
     *
205
     * @return array<boolean|integer|array> Return parsed result
0 ignored issues
show
Documentation introduced by
Should the return type not be array<integer|array<bool...|integer|array>|string>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
206
     */
207 37
    public static function subexpression($expression, &$context) {
208 37
        $context['usedFeature']['subexp']++;
209 37
        $vars = static::analyze(substr($expression, 1, -1), $context);
210 37
        $avars = static::advancedVariable($vars, $context, $expression);
211 37
        if (isset($avars[0][0]) && !$context['flags']['exhlp']) {
212 21
            if (!Validator::helper($context, $avars[0][0])) {
213 2
                $context['error'][] = "Can not find custom helper function defination {$avars[0][0]}() !";
214
            }
215
        }
216 37
        return array(-1, $avars, $expression);
217
    }
218
219
    /**
220
     * Check a parsed result is a subexpression or not
221
     *
222
     * @param array<string|integer|array> $var
223
     *
224
     * @return boolean return true when input is a subexpression
225
     */
226 354
    public static function isSubExp($var) {
227 354
        return is_array($var) && (count($var) === 3) && ($var[0] === -1) && is_string($var[2]);
228
    }
229
230
    /**
231
     * Check a parsed result is a subexpression or not
232
     *
233
     * @param array<string|integer|array> $var
234
     *
235
     * @return boolean return true when input is a subexpression
236
     */
237
    public static function isBlockParam($var) {
238
        return is_array($var) && (count($var) === 3) && ($var[0] === -2) && is_string($var[2]);
239
    }
240
241
    /**
242
     * Analyze parsed token for advanved variables.
243
     *
244
     * @param array<boolean|integer|array> $vars parsed token
245
     * @param array<string,array|string|integer> $context current compile context
246
     * @param string $token original token
247
     *
248
     * @return array<boolean|integer|array> Return parsed result
249
     *
250
     */
251 588
    protected static function advancedVariable($vars, &$context, $token) {
252 588
        $ret = array();
253 588
        $i = 0;
254 588
        foreach ($vars as $idx => $var) {
255
            // handle (...)
1 ignored issue
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...
256 582
            if (preg_match(SafeString::IS_SUBEXP_SEARCH, $var)) {
257 34
                $ret[$i] = static::subexpression($var, $context);
258 34
                $i++;
259 34
                continue;
260
            }
261
262
            // handle |...|
1 ignored issue
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...
263 582
            if (preg_match(SafeString::IS_BLOCKPARAM_SEARCH, $var, $matched)) {
264 5
                $ret[static::BLOCKPARAM] = explode(' ', $matched[1]);
265 5
                continue;
266
            }
267
268 582
            if ($context['flags']['namev']) {
269 379
                if (preg_match('/^((\\[([^\\]]+)\\])|([^=^["\']+))=(.+)$/', $var, $m)) {
270 32
                    if (!$context['flags']['advar'] && $m[3]) {
271 1
                        $context['error'][] = "Wrong argument name as '[$m[3]]' in $token ! You should fix your template or compile with LightnCandy::FLAG_ADVARNAME flag.";
272
                    }
273 32
                    $idx = $m[3] ? $m[3] : $m[4];
274 32
                    $var = $m[5];
275
                    // handle foo=(...)
1 ignored issue
show
Unused Code Comprehensibility introduced by
38% 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...
276 32
                    if (preg_match(SafeString::IS_SUBEXP_SEARCH, $var)) {
277 4
                        $ret[$idx] = static::subexpression($var, $context);
278 4
                        continue;
279
                    }
280
                }
281
            }
282
283 582
            if ($context['flags']['advar'] && !preg_match("/^(\"|\\\\')(.*)(\"|\\\\')$/", $var)) {
284
                    // foo]  Rule 1: no starting [ or [ not start from head
285 394
                if (preg_match('/^[^\\[\\.]+[\\]\\[]/', $var)
286
                    // [bar  Rule 2: no ending ] or ] not in the end
287 388
                    || preg_match('/[\\[\\]][^\\]\\.]+$/', $var)
288
                    // ]bar. Rule 3: middle ] not before .
289 386
                    || preg_match('/\\][^\\]\\[\\.]+\\./', $var)
290
                    // .foo[ Rule 4: middle [ not after .
291 394
                    || preg_match('/\\.[^\\]\\[\\.]+\\[/', preg_replace('/^(..\\/)+/', '', preg_replace('/\\[[^\\]]+\\]/', '[XXX]', $var)))
292
                ) {
293 12
                    $context['error'][] = "Wrong variable naming as '$var' in $token !";
294
                } else {
295 382
                    $name = preg_replace('/(\\[.+?\\])/', '', $var);
296
                    // Scan for invalid charactors which not be protected by [ ]
297
                    // now make ( and ) pass, later fix
298 382
                    if (preg_match('/[!"#%\'*+,;<=>{|}~]/', $name)) {
299 2
                        $context['error'][] = "Wrong variable naming as '$var' in $token ! You should wrap ! \" # % & ' * + , ; < = > { | } ~ into [ ]";
300
                    }
301
                }
302
            }
303
304 582
            $var = static::getExpression($var, $context, (count($vars) === 1) && ($idx === 0));
305
306 582
            if (is_string($idx)) {
307 29
                $ret[$idx] = $var;
308
            } else {
309 581
                $ret[$i] = $var;
310 582
                $i++;
311
            }
312
        }
313 588
        return $ret;
314
    }
315
316
    /**
317
     * Analyze a token string and return parsed result.
318
     *
319
     * @param string $token preg_match results
320
     * @param array<string,array|string|integer> $context current compile context
321
     *
322
     * @return array<boolean|integer|array> Return parsed result
323
     *
324
     */
325 588
    protected static function analyze($token, &$context) {
326 588
        $count = preg_match_all('/(\s*)([^\s]+)/', $token, $matchedall);
327
        // Parse arguments and deal with "..." or [...] or (...) or \'...\' or |...|
1 ignored issue
show
Unused Code Comprehensibility introduced by
46% 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...
328 588
        if (($count > 0) && $context['flags']['advar']) {
329 407
            $vars = array();
330 407
            $prev = '';
331 407
            $expect = 0;
332 407
            $stack = 0;
333
334 407
            foreach ($matchedall[2] as $index => $t) {
335
                // continue from previous match when expect something
336 407
                if ($expect) {
337 45
                    $prev .= "{$matchedall[1][$index]}$t";
338 45
                    if (($stack > 0) && (substr($t, 0, 1) === '(')) {
339 9
                        $stack++;
340
                    }
341
                    // end an argument when end with expected charactor
342 45
                    if (substr($t, -1, 1) === $expect) {
343 45
                        if ($stack > 0) {
344 35
                            preg_match('/(\\)+)$/', $t, $matchedq);
345 35
                            $stack -= isset($matchedq[0]) ? strlen($matchedq[0]) : 1;
346 35
                            if ($stack > 0) {
347 3
                                continue;
348
                            }
349 35
                            if ($stack < 0) {
350 1
                                $context['error'][] = "Unexcepted ')' in expression '$token' !!";
351 1
                                break;
352
                            }
353
                        }
354 44
                        $vars[] = $prev;
355 44
                        $prev = '';
356 44
                        $expect = 0;
357
                    }
358 45
                    continue;
359
                }
360
361
                // continue to next match when begin with '(' without ending ')'
362 407
                if (preg_match('/^\([^\)]*$/', $t)) {
363 28
                    $prev = $t;
364 28
                    $expect = ')';
365 28
                    $stack=1;
366 28
                    continue;
367
                }
368
369
                // continue to next match when begin with '"' without ending '"'
370 407
                if (preg_match('/^"[^"]*$/', $t)) {
371 5
                    $prev = $t;
372 5
                    $expect = '"';
373 5
                    continue;
374
                }
375
376
                // continue to next match when begin with \' without ending '
377 406
                if (preg_match('/^\\\\\'[^\']*$/', $t)) {
378 6
                    $prev = $t;
379 6
                    $expect = '\'';
380 6
                    continue;
381
                }
382
383
                // continue to next match when '="' exists without ending '"'
384 405
                if (preg_match('/^[^"]*="[^"]*$/', $t)) {
385 2
                    $prev = $t;
386 2
                    $expect = '"';
387 2
                    continue;
388
                }
389
390
                // continue to next match when '[' exists without ending ']'
391 405
                if (preg_match('/^([^"\'].+)?\\[[^\\]]*$/', $t)) {
392 9
                    $prev = $t;
393 9
                    $expect = ']';
394 9
                    continue;
395
                }
396
397
                // continue to next match when =\' exists without ending '
398 397
                if (preg_match('/^[^\']*=\\\\\'[^\']*$/', $t)) {
399 1
                    $prev = $t;
400 1
                    $expect = '\'';
401 1
                    continue;
402
                }
403
404
                // continue to next match when =( exists without ending )
405 397
                if (preg_match('/.+\([^\)]*$/', $t)) {
406 3
                    $prev = $t;
407 3
                    $expect = ')';
408 3
                    $stack=1;
409 3
                    continue;
410
                }
411
412
                // continue to next match when 'as' without ending '|'
413 397
                if (($t === 'as') && (count($vars) > 0)) {
414 5
                    $prev = '';
415 5
                    $expect = '|';
416 5
                    $stack=1;
417 5
                    continue;
418
                }
419
420 397
                $vars[] = $t;
421
            }
422 407
            return $vars;
423
        }
424 204
        return ($count > 0) ? $matchedall[2] : explode(' ', $token);
425
    }
426
}
427
428