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 — master ( 030069...18e114 )
by Zordius
02:27
created

Validator::resolveHelper()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 11
cts 11
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 5
nop 2
crap 5
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-2016 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 Validator
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
use \LightnCandy\Token;
23
use \LightnCandy\Parser;
24
use \LightnCandy\Partial;
25
use \LightnCandy\Expression;
26
use \LightnCandy\SafeString;
27
28
/**
29
 * LightnCandy Validator
30
 */
31
class Validator {
32
    /**
33
     * Verify template
34
     *
35
     * @param array<string,array|string|integer> $context Current context
36
     * @param string $template handlebars template
37
     */
38 758
    public static function verify(&$context, $template) {
39 758
        $template = SafeString::stripExtendedComments($template);
40 758
        $context['level'] = 0;
41 758
        Parser::setDelimiter($context);
42
43 758
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 747
            if ($context['flags']['slash'] && ($matches[Token::POS_LSPACE] === '') && preg_match('/^(.*?)(\\\\+)$/s', $matches[Token::POS_LOTHER], $escmatch)) {
46 4
                if (strlen($escmatch[2]) % 4) {
47 2
                    static::pushToken($context, substr($matches[Token::POS_LOTHER], 0, -2) . $context['tokens']['startchar']);
48 2
                    $matches[Token::POS_BEGINTAG] = substr($matches[Token::POS_BEGINTAG], 1);
49 2
                    $template = implode('', array_slice($matches, Token::POS_BEGINTAG));
50 2
                    continue;
51
                } else {
52 2
                    $matches[Token::POS_LOTHER] = $escmatch[1] . str_repeat('\\', strlen($escmatch[2]) / 2);
53
                }
54
            }
55 745
            $context['tokens']['count']++;
56 745
            $V = static::token($matches, $context);
57 745
            static::pushLeft($context);
58 745
            if ($V) {
59 706
                if (is_array($V)) {
60 699
                    array_push($V, $matches, $context['tokens']['partialind']);
61
                }
62 706
                static::pushToken($context, $V);
63
            }
64 745
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
65
        }
66 758
        static::pushToken($context, $template);
67
68 758
        if ($context['level'] > 0) {
69 8
            array_pop($context['stack']);
70 8
            array_pop($context['stack']);
71 8
            $token = array_pop($context['stack']);
72 8
            $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : ( $context['partialblock'] ? "{{#>{$token}}}" : "{{#{$token}}}")) . ' !!';
73
        }
74 758
    }
75
76
    /**
77
     * push left string of current token and clear it
78
     *
79
     * @param array<string,array|string|integer> $context Current context
80
     */
81 745
    protected static function pushLeft(&$context) {
82 745
        $L = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
83
84 745
        if ($context['currentToken'][Token::POS_OP] === '!') {
85
            $appender = function (&$pb) use ($context, $L) {
86 1
                $pb .= $L;
87 27
            };
88 27
            if (count($context['partialblock']) > 0) {
89 1
                array_walk($context['partialblock'], $appender);
90
            }
91 27
            if (count($context['inlinepartial']) > 0) {
92
                array_walk($context['inlinepartial'], $appender);
93
            }
94
        }
95
96 745
        static::pushToken($context, $L);
97 745
        $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = '';
98 745
    }
99
100
    /**
101
     * push a token into the stack when it is not empty string
102
     *
103
     * @param array<string,array|string|integer> $context Current context
104
     * @param string|array $token a parsed token or a string
105
     */
106 758
    protected static function pushToken(&$context, $token) {
107 758
        if ($token === '') {
108 622
            return;
109
        }
110 747
        if (is_string($token)) {
111 504
            if (is_string(end($context['parsed'][0]))) {
112 31
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
113 31
                return;
114
            }
115
        }
116 747
        $context['parsed'][0][] = $token;
117 747
    }
118
119
    /**
120
     * push current token into the section stack
121
     *
122
     * @param array<string,array|string|integer> $context Current context
123
     * @param string $operation operation string
124
     * @param array<boolean|integer|string|array> $vars parsed arguments list
125
     */
126 374
    protected static function pushStack(&$context, $operation, $vars) {
127 374
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
128 374
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
129 374
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
130 374
        $context['stack'][] = $operation;
131 374
        $context['level']++;
132 374
    }
133
134
    /**
135
     * Verify delimiters and operators
136
     *
137
     * @param string[] $token detected handlebars {{ }} token
138
     * @param array<string,array|string|integer> $context current compile context
139
     *
140
     * @return boolean|null Return true when invalid
141
     *
142
     * @expect null when input array_fill(0, 11, ''), array()
143
     * @expect null when input array(0, 0, 0, 0, 0, '{{', '#', '...', '}}'), array()
144
     * @expect true when input array(0, 0, 0, 0, 0, '{', '#', '...', '}'), array()
145
     */
146 746
    protected static function delimiter($token, &$context) {
147
        // {{ }}} or {{{ }} are invalid
148 746
        if (strlen($token[Token::POS_BEGINRAW]) !== strlen($token[Token::POS_ENDRAW])) {
149 6
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' or ' . Token::toString($token, array(Token::POS_BEGINRAW => '{', Token::POS_ENDRAW => '}')) . '?';
150 6
            return true;
151
        }
152
        // {{{# }}} or {{{! }}} or {{{/ }}} or {{{^ }}} are invalid.
153 740
        if ((strlen($token[Token::POS_BEGINRAW]) == 1) && $token[Token::POS_OP] && ($token[Token::POS_OP] !== '&')) {
154 5
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' ?';
155 5
            return true;
156
        }
157 736
    }
158
159
    /**
160
     * Verify operators
161
     *
162
     * @param string $operator the operator string
163
     * @param array<string,array|string|integer> $context current compile context
164
     * @param array<boolean|integer|string|array> $vars parsed arguments list
165
     *
166
     * @return boolean|integer|null Return true when invalid or detected
167
     *
168
     * @expect null when input '', array(), array()
169
     * @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'elselvl' => array(), 'flags' => array('spvar' => 0), 'elsechain' => false, 'helperresolver' => 0), array(array('foo'))
170
     * @expect true when input '/', array('stack' => array('[with]', '#'), 'level' => 1, 'currentToken' => array(0,0,0,0,0,0,0,'with'), 'flags' => array('nohbh' => 0)), array(array())
171
     * @expect 4 when input '#', array('usedFeature' => array('sec' => 3), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('x'))
172
     * @expect 5 when input '#', array('usedFeature' => array('if' => 4), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('if'))
173
     * @expect 6 when input '#', array('usedFeature' => array('with' => 5), 'level' => 0, 'flags' => array('nohbh' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('with'))
174
     * @expect 7 when input '#', array('usedFeature' => array('each' => 6), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('each'))
175
     * @expect 8 when input '#', array('usedFeature' => array('unless' => 7), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('unless'))
176
     * @expect 9 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 8), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array()), array(array('abc'))
177
     * @expect 11 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 10), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array()), array(array('abc'))
178
     * @expect true when input '>', array('partialresolver' => false, 'usedFeature' => array('partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elsechain' => false, 'elselvl' => array()), array('test')
179
     */
180 707
    protected static function operator($operator, &$context, &$vars) {
181
        switch ($operator) {
182 707
            case '#*':
183 14
                if (!$context['compile']) {
184 14
                    static::pushLeft($context);
185 14
                    $context['stack'][] = count($context['parsed'][0]);
186 14
                    static::pushStack($context, '#*', $vars);
187 14
                    array_unshift($context['inlinepartial'], '');
188
                }
189 14
                return static::inline($context, $vars);
190
191 704
            case '#>':
192 22
                if (!$context['compile']) {
193 22
                    static::pushLeft($context);
194 22
                    $context['stack'][] = count($context['parsed'][0]);
195 22
                    $vars[Parser::PARTIALBLOCK] = ++$context['usedFeature']['pblock'];
196 22
                    static::pushStack($context, '#>', $vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer|string,boo...er|string|array|double>, but the function expects a array<integer,boolean|integer|string|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...
197 22
                    array_unshift($context['partialblock'], '');
198
                }
199 22
                return static::partial($context, $vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer|string,boo...er|string|array|double>, but the function expects a array<integer,boolean|integer|string|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...
200
201 700
            case '>':
202 99
                return static::partial($context, $vars);
203
204 661
            case '^':
205 64
                if (!isset($vars[0][0])) {
206 24
                    if (!$context['flags']['else']) {
207 1
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
208 1
                        return;
209
                    } else {
210 23
                        return static::doElse($context, $vars);
211
                    }
212
                }
213
214 40
                static::doElseChain($context);
215
216 40
                if (static::isBlockHelper($context, $vars)) {
217 1
                    static::pushStack($context, '#', $vars);
218 1
                    return static::blockCustomHelper($context, $vars, true);
219
                }
220
221 39
                static::pushStack($context, '^', $vars);
222 39
                return static::invertedSection($context, $vars);
223
224 661
            case '/':
225 347
                $r = static::blockEnd($context, $vars);
226 347
                if ($r !== Token::POS_BACKFILL) {
227 347
                    array_pop($context['stack']);
228 347
                    array_pop($context['stack']);
229 347
                    array_pop($context['stack']);
230
                }
231 347
                return $r;
232
233 636
            case '#':
234 323
                static::doElseChain($context);
235 323
                static::pushStack($context, '#', $vars);
236
237 323
                if (static::isBlockHelper($context, $vars)) {
238 63
                    return static::blockCustomHelper($context, $vars);
239
                }
240
241 269
                return static::blockBegin($context, $vars);
242
        }
243 536
    }
244
245
    /**
246
     * validate inline partial begin token
247
     *
248
     * @param array<string,array|string|integer> $context current compile context
249
     * @param array<boolean|integer|string|array> $vars parsed arguments list
250
     *
251
     * @return boolean|null Return true when inline partial ends
252
     */
253 706
    protected static function inlinePartial(&$context, $vars) {
254 706
        if (count($context['inlinepartial']) > 0) {
255 14
            $ended = false;
256 14
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
257
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
258 14
                $pb .= $append;
259 14
            });
260 14
            if ($context['currentToken'][Token::POS_OP] === '/') {
261 14
                if (static::blockEnd($context, $vars, '#*') !== null) {
262 14
                    $context['usedFeature']['inlpartial']++;
263 14
                    $tmpl = array_shift($context['inlinepartial']);
264 14
                    $c = $context['stack'][count($context['stack']) - 4];
265 14
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
266 14
                    $P = &$context['parsed'][0][$c];
267 14
                    if (isset($P[1][1][0])) {
268 13
                        $context['usedPartial'][$P[1][1][0]] = $tmpl;
269 13
                        $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]);
270
                    }
271 14
                    $ended = true;
272
                }
273
            }
274 14
            $append = Token::toString($context['currentToken']);
275
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
276 3
                $pb .= $append;
277 14
            });
278 14
            return $ended;
279
        }
280 706
    }
281
282
    /**
283
     * validate partial block token
284
     *
285
     * @param array<string,array|string|integer> $context current compile context
286
     * @param array<boolean|integer|string|array> $vars parsed arguments list
287
     *
288
     * @return boolean|null Return true when partial block ends
289
     */
290 706
    protected static function partialBlock(&$context, $vars) {
291 706
        if (count($context['partialblock']) > 0) {
292 21
            $ended = false;
293 21
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
294
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
295 21
                $pb .= $append;
296 21
            });
297 21
            if ($context['currentToken'][Token::POS_OP] === '/') {
298 21
                if (static::blockEnd($context, $vars, '#>') !== null) {
299 21
                    $c = $context['stack'][count($context['stack']) - 4];
300 21
                    $found = Partial::resolve($context, $vars[0][0]) !== null;
301 21
                    $v = $found ? "@partial-block{$context['parsed'][0][$c][1][Parser::PARTIALBLOCK]}" : "{$vars[0][0]}";
302 21
                    if ($found) {
303 17
                        $context['partials'][$v] = $context['partialblock'][0];
304
                    }
305 21
                    $context['usedPartial'][$v] = $context['partialblock'][0];
306 21
                    Partial::compileDynamic($context, $v);
307 21
                    if ($found) {
308 17
                        Partial::read($context, $vars[0][0]);
309
                    }
310 21
                    array_shift($context['partialblock']);
311 21
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
312 21
                    $ended = true;
313
                }
314
            }
315 21
            $append = Token::toString($context['currentToken']);
316 21
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
317 15
                $pb .= $append;
318 21
            });
319 21
            return $ended;
320
        }
321 706
    }
322
323
    /**
324
     * handle else chain
325
     *
326
     * @param array<string,array|string|integer> $context current compile context
327
     */
328 352
    protected static function doElseChain(&$context) {
329 352
        if ($context['elsechain']) {
330 12
            $context['elsechain'] = false;
331
        } else {
332 352
            array_unshift($context['elselvl'], array());
333
        }
334 352
    }
335
336
    /**
337
     * validate block begin token
338
     *
339
     * @param array<string,array|string|integer> $context current compile context
340
     * @param array<boolean|integer|string|array> $vars parsed arguments list
341
     *
342
     * @return boolean Return true always
343
     */
344 268
    protected static function blockBegin(&$context, $vars) {
345 268
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
346 268
            case 'with':
347 34
                return static::with($context, $vars);
348 241
            case 'each':
349 56
                return static::section($context, $vars, true);
350 196
            case 'unless':
351 7
                return static::unless($context, $vars);
352 190
            case 'if':
353 77
                return static::doIf($context, $vars);
354
            default:
355 120
                return static::section($context, $vars);
356
        }
357
    }
358
359
    /**
360
     * validate builtin helpers
361
     *
362
     * @param array<string,array|string|integer> $context current compile context
363
     * @param array<boolean|integer|string|array> $vars parsed arguments list
364
     */
365 157
    protected static function builtin(&$context, $vars) {
366 157
        if ($context['flags']['nohbh']) {
367 8
            if (isset($vars[1][0])) {
368 8
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
369
            }
370
        } else {
371 149
            if (count($vars) < 2) {
372 5
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
373
            }
374
        }
375 157
        $context['usedFeature'][$vars[0][0]]++;
376 157
    }
377
378
    /**
379
     * validate section token
380
     *
381
     * @param array<string,array|string|integer> $context current compile context
382
     * @param array<boolean|integer|string|array> $vars parsed arguments list
383
     * @param boolean $isEach the section is #each
384
     *
385
     * @return boolean Return true always
386
     */
387 176
    protected static function section(&$context, $vars, $isEach = false) {
388 176
        if ($isEach) {
389 56
            static::builtin($context, $vars);
390
        } else {
391 120
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
392 1
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
393
            }
394 120
            $context['usedFeature']['sec']++;
395
        }
396 176
        return true;
397
    }
398
399
    /**
400
     * validate with token
401
     *
402
     * @param array<string,array|string|integer> $context current compile context
403
     * @param array<boolean|integer|string|array> $vars parsed arguments list
404
     *
405
     * @return boolean Return true always
406
     */
407 34
    protected static function with(&$context, $vars) {
408 34
        static::builtin($context, $vars);
409 34
        return true;
410
    }
411
412
    /**
413
     * validate unless token
414
     *
415
     * @param array<string,array|string|integer> $context current compile context
416
     * @param array<boolean|integer|string|array> $vars parsed arguments list
417
     *
418
     * @return boolean Return true always
419
     */
420 7
    protected static function unless(&$context, $vars) {
421 7
        static::builtin($context, $vars);
422 7
        return true;
423
    }
424
425
    /**
426
     * validate if token
427
     *
428
     * @param array<string,array|string|integer> $context current compile context
429
     * @param array<boolean|integer|string|array> $vars parsed arguments list
430
     *
431
     * @return boolean Return true always
432
     */
433 77
    protected static function doIf(&$context, $vars) {
434 77
        static::builtin($context, $vars);
435 77
        return true;
436
    }
437
438
    /**
439
     * validate block custom helper token
440
     *
441
     * @param array<string,array|string|integer> $context current compile context
442
     * @param array<boolean|integer|string|array> $vars parsed arguments list
443
     * @param boolean $inverted the logic will be inverted
444
     *
445
     * @return integer|null Return number of used custom helpers
446
     */
447 63
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
448 63
        if (is_string($vars[0][0])) {
449 63
            if (static::resolveHelper($context, $vars)) {
450 63
                return ++$context['usedFeature']['helper'];
451
            }
452
        }
453
    }
454
455
    /**
456
     * validate inverted section
457
     *
458
     * @param array<string,array|string|integer> $context current compile context
459
     * @param array<boolean|integer|string|array> $vars parsed arguments list
460
     *
461
     * @return integer Return number of inverted sections
462
     */
463 38
    protected static function invertedSection(&$context, $vars) {
464 38
        return ++$context['usedFeature']['isec'];
465
    }
466
467
    /**
468
     * Return compiled PHP code for a handlebars block end token
469
     *
470
     * @param array<string,array|string|integer> $context current compile context
471
     * @param array<boolean|integer|string|array> $vars parsed arguments list
472
     * @param string|null $match should also match to this operator
473
     *
474
     * @return boolean Return true
475
     */
476 367
    protected static function blockEnd(&$context, &$vars, $match = null) {
477 367
        $context['level']--;
478 367
        $c = count($context['stack']) - 2;
479 367
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
480 367
        if (($match !== null) && ($match !== $pop)) {
481 6
            return;
482
        }
483 367
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
484 367
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
485 367
            case 'with':
486 38
                if (!$context['flags']['nohbh']) {
487 36
                    if ($pop2 !== '[with]') {
488 1
                        $context['error'][] = 'Unexpect token: {{/with}} !';
489 1
                        return;
490
                    }
491
                }
492 37
                return true;
493
        }
494
495
        switch($pop) {
496 347
            case '#':
497 70
            case '^':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
498 318
                $elsechain = array_shift($context['elselvl']);
499 318
                if (isset($elsechain[0])) {
500 12
                    $context['currentToken'][Token::POS_RSPACE] = $context['currentToken'][Token::POS_BACKFILL] = '{{/' . implode('}}{{/', $elsechain) . '}}' . Token::toString($context['currentToken']) . $context['currentToken'][Token::POS_RSPACE];
501 12
                    return Token::POS_BACKFILL;
502
                }
503 32
            case '#>':
504 15
            case '#*':
505 346
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
506 346
                $v = Expression::toString($levels, $spvar, $var);
507 346
                if ($pop2 !== $v) {
508 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
509 2
                    return;
510
                }
511 345
                return true;
512
            default:
513 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
514 1
                return;
515
        }
516
    }
517
518
    /**
519
     * handle delimiter change
520
     *
521
     * @param array<string,array|string|integer> $context current compile context
522
     *
523
     * @return boolean|null Return true when delimiter changed
524
     */
525 735
    protected static function isDelimiter(&$context) {
526 735
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
527 15
            $context['usedFeature']['delimiter']++;
528 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
529 15
            return true;
530
        }
531 726
    }
532
533
    /**
534
     * handle raw block
535
     *
536
     * @param string[] $token detected handlebars {{ }} token
537
     * @param array<string,array|string|integer> $context current compile context
538
     *
539
     * @return boolean|null Return true when in rawblock mode
540
     */
541 745
    protected static function rawblock(&$token, &$context) {
542 745
        $inner = $token[Token::POS_INNERTAG];
543 745
        trim($inner);
544
545
        // skip parse when inside raw block
546 745
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
547 4
            return true;
548
        }
549
550 745
        $token[Token::POS_INNERTAG] = $inner;
551
552
        // Handle raw block
553 745
        if ($token[Token::POS_BEGINRAW] === '{{') {
554 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
555 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
556
            }
557 7
            if ($context['rawblock']) {
558 4
                Parser::setDelimiter($context);
559 4
                $context['rawblock'] = false;
560
            } else {
561 7
                if ($token[Token::POS_OP]) {
562 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
563
                }
564 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
565 7
                Parser::setDelimiter($context);
566 7
                $token[Token::POS_OP] = '#';
567
            }
568 7
            $token[Token::POS_ENDRAW] = '}}';
569
        }
570 745
    }
571
572
    /**
573
     * handle comment
574
     *
575
     * @param string[] $token detected handlebars {{ }} token
576
     * @param array<string,array|string|integer> $context current compile context
577
     *
578
     * @return boolean|null Return true when is comment
579
     */
580 726
    protected static function comment(&$token, &$context) {
581 726
        if ($token[Token::POS_OP] === '!') {
582 26
            $context['usedFeature']['comment']++;
583 26
            return true;
584
        }
585 706
    }
586
587
    /**
588
     * Collect handlebars usage information, detect template error.
589
     *
590
     * @param string[] $token detected handlebars {{ }} token
591
     * @param array<string,array|string|integer> $context current compile context
592
     */
593 745
    protected static function token(&$token, &$context) {
594 745
        $context['currentToken'] = &$token;
595
596 745
        if (static::rawblock($token, $context)) {
597 4
            return Token::toString($token);
598
        }
599
600 745
        if (static::delimiter($token, $context)) {
601 10
            return;
602
        }
603
604 735
        if (static::isDelimiter($context)) {
605 15
            static::spacing($token, $context);
606 15
            return;
607
        }
608
609 726
        if (static::comment($token, $context)) {
610 26
            static::spacing($token, $context);
611 26
            return;
612
        }
613
614 706
        list($raw, $vars) = Parser::parse($token, $context);
615
616 706
        $partials = static::partialBlock($context, $vars);
617 706
        $partials = static::inlinePartial($context, $vars) || $partials;
618
619 706
        if ($partials) {
620 31
            $context['stack'] = array_slice($context['stack'], 0, -4);
621 31
            $context['currentToken'][Token::POS_LOTHER] = '';
622 31
            $context['currentToken'][Token::POS_LSPACE] = '';
623 31
            return;
624
        }
625
626
        // Handle spacing (standalone tags, partial indent)
627 706
        static::spacing($token, $context, (($token[Token::POS_OP] === '') || ($token[Token::POS_OP] === '&')) && (!$context['flags']['else'] || !isset($vars[0][0]) || ($vars[0][0] !== 'else')) || ($context['flags']['nostd'] > 0));
628
629 706
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
630 426
            return isset($token[Token::POS_BACKFILL]) ? null : array($raw, $vars);
631
        }
632
633 539
        if (count($vars) == 0) {
634 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
635
        }
636
637 533
        if (!isset($vars[0])) {
638 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
639
        }
640
641 532
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
642
643 532
        foreach ($vars as $var) {
644 532
            if (!isset($var[0]) || ($var[0] === 0)) {
645 73
                if ($context['level'] == 0) {
646 25
                    $context['usedFeature']['rootthis']++;
647
                }
648 532
                $context['usedFeature']['this']++;
649
            }
650
        }
651
652 532
        if (!isset($vars[0][0])) {
653 54
            return array($raw, $vars);
654
        }
655
656 498
        if (($vars[0][0] === 'else') && $context['flags']['else']) {
657 36
            static::doElse($context, $vars);
658 36
            return array($raw, $vars);
659
        }
660
661 474
        if (!static::helper($context, $vars)) {
662 359
            static::lookup($context, $vars);
663 359
            static::log($context, $vars);
664
        }
665
666 474
        return array($raw, $vars);
667
    }
668
669
    /**
670
     * Return 1 or larger number when else token detected
671
     *
672
     * @param array<string,array|string|integer> $context current compile context
673
     * @param array<boolean|integer|string|array> $vars parsed arguments list
674
     *
675
     * @return integer Return 1 or larger number when else token detected
676
     */
677 58
    protected static function doElse(&$context, $vars) {
678 58
        if ($context['level'] == 0) {
679 1
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
680
        }
681
682 58
        if (isset($vars[1][0])) {
683 12
            $token = $context['currentToken'];
684 12
            $context['currentToken'][Token::POS_RSPACE] = "{{#{$vars[1][0]} " . preg_replace('/^\\s*else\\s+' . $vars[1][0] . '\\s*/', '', $token[Token::POS_INNERTAG]) . '}}' . $context['currentToken'][Token::POS_RSPACE];
685 12
            array_unshift($context['elselvl'][0], $vars[1][0]);
686 12
            $context['elsechain'] = true;
687
        }
688
689 58
        return ++$context['usedFeature']['else'];
690
    }
691
692
    /**
693
     * Return true when this is {{log ...}}
694
     *
695
     * @param array<string,array|string|integer> $context current compile context
696
     * @param array<boolean|integer|string|array> $vars parsed arguments list
697
     *
698
     * @return boolean|null Return true when it is custom helper
699
     */
700 359
    public static function log(&$context, $vars) {
701 359
        if (isset($vars[0][0]) && ($vars[0][0] === 'log')) {
702 3
            if (!$context['flags']['nohbh']) {
703 3
                if (count($vars) < 2) {
704 1
                    $context['error'][] = "No argument after {{log}} !";
705
                }
706 3
                $context['usedFeature']['log']++;
707 3
                return true;
708
            }
709
        }
710 356
    }
711
712
    /**
713
     * Return true when this is {{lookup ...}}
714
     *
715
     * @param array<string,array|string|integer> $context current compile context
716
     * @param array<boolean|integer|string|array> $vars parsed arguments list
717
     *
718
     * @return boolean|null Return true when it is custom helper
719
     */
720 359
    public static function lookup(&$context, $vars) {
721 359
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
722 6
            if (!$context['flags']['nohbh']) {
723 6
                if (count($vars) < 2) {
724 1
                    $context['error'][] = "No argument after {{lookup}} !";
725 5
                } else if (count($vars) < 3) {
726 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
727
                }
728 6
                $context['usedFeature']['lookup']++;
729 6
                return true;
730
            }
731
        }
732 353
    }
733
734
    /**
735
     * Return true when the name is listed in helper table
736
     *
737
     * @param array<string,array|string|integer> $context current compile context
738
     * @param array<boolean|integer|string|array> $vars parsed arguments list
739
     * @param boolean $checkSubexp true when check for subexpression
740
     *
741
     * @return boolean Return true when it is custom helper
742
     */
743 482
    public static function helper(&$context, $vars, $checkSubexp = false) {
744 482
        if (static::resolveHelper($context, $vars)) {
745 130
            $context['usedFeature']['helper']++;
746 130
            return true;
747
        }
748
749 362
        if ($checkSubexp) {
750 4
            switch ($vars[0][0]) {
751 4
            case 'if':
752 4
            case 'unless':
753 4
            case 'with':
754 4
            case 'each':
755 4
            case 'lookup':
756 2
                return $context['flags']['nohbh'] ? false : true;
757
            }
758
        }
759
760 361
        return false;
761
    }
762
763
    /**
764
     * use helperresolver to resolve helper, return true when helper founded
765
     *
766
     * @param array<string,array|string|integer> $context Current context of compiler progress.
767
     * @param array<boolean|integer|string|array> $vars parsed arguments list
768
     *
769
     * @return boolean $found helper exists or not
770
     */
771 644
    public static function resolveHelper(&$context, &$vars) {
772 644
        if (count($vars[0]) !== 1) {
773 97
            return false;
774
        }
775 587
        if (isset($context['helpers'][$vars[0][0]])) {
776 181
            return true;
777
        }
778
779 456
        if ($context['helperresolver']) {
780 3
            $helper = $context['helperresolver']($context, $vars[0][0]);
781 3
            if ($helper) {
782 3
                $context['helpers'][$vars[0][0]] = $helper;
783 3
                return true;
784
            }
785
        }
786
787 453
        return false;
788
    }
789
790
    /**
791
     * detect for block custom helper
792
     *
793
     * @param array<string,array|string|integer> $context current compile context
794
     * @param array<boolean|integer|string|array> $vars parsed arguments list
795
     *
796
     * @return boolean|null Return true when this token is block custom helper
797
     */
798 352
    protected static function isBlockHelper($context, $vars) {
799 352
        if (!isset($vars[0][0])) {
800 4
            return;
801
        }
802
803 349
        if (!static::resolveHelper($context, $vars)) {
804 294
            return;
805
        }
806
807 63
        return true;
808
    }
809
810
    /**
811
     * validate inline partial
812
     *
813
     * @param array<string,array|string|integer> $context current compile context
814
     * @param array<boolean|integer|string|array> $vars parsed arguments list
815
     *
816
     * @return boolean Return true always
817
     */
818 14
    protected static function inline(&$context, $vars) {
819 14
        if (!$context['flags']['runpart']) {
820 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
821
        }
822 14
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
823 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
824
        }
825 14
        if (!isset($vars[1][0])) {
826 1
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
827
        }
828 14
        return true;
829
    }
830
831
    /**
832
     * validate partial
833
     *
834
     * @param array<string,array|string|integer> $context current compile context
835
     * @param array<boolean|integer|string|array> $vars parsed arguments list
836
     *
837
     * @return integer|boolean Return 1 or larger number for runtime partial, return true for other case
838
     */
839 105
    protected static function partial(&$context, $vars) {
840 105
        if (Parser::isSubExp($vars[0])) {
841 6
            if ($context['flags']['runpart']) {
842 5
                return $context['usedFeature']['dynpartial']++;
843
            } else {
844 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
845 1
                return true;
846
            }
847
        } else {
848 99
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
849 92
                Partial::read($context, $vars[0][0]);
850
            }
851
        }
852 99
        if (!$context['flags']['runpart']) {
853 11
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
854 11
            if ($named || (count($vars) > 1)) {
855 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
856
            }
857
        }
858
859 99
        return true;
860
    }
861
862
    /**
863
     * Modify $token when spacing rules matched.
864
     *
865
     * @param array<string> $token detected handlebars {{ }} token
866
     * @param array<string,array|string|integer> $context current compile context
867
     * @param boolean $nost do not do stand alone logic
868
     *
869
     * @return string|null Return compiled code segment for the token
870
     */
871 735
    protected static function spacing(&$token, &$context, $nost = false) {
872
        // left line change detection
873 735
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
874 735
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
875
        // right line change detection
876 735
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
877 735
        $st = true;
878
        // setup ahead flag
879 735
        $ahead = $context['tokens']['ahead'];
880 735
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
881
        // reset partial indent
882 735
        $context['tokens']['partialind'] = '';
883
        // same tags in the same line , not standalone
884 735
        if (!$lsp && $ahead) {
885 361
            $st = false;
886
        }
887 735
        if ($nost) {
888 517
            $st = false;
889
        }
890
        // not standalone because other things in the same line ahead
891 735
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
892 217
            $st = false;
893
        }
894
        // not standalone because other things in the same line behind
895 735
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
896 367
            $st = false;
897
        }
898 735
        if ($st && (($lsp && $rsp) // both side cr
899 129
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
900 735
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
901
           )) {
902
            // handle partial
903 63
            if ($token[Token::POS_OP] === '>') {
904 15
                if (!$context['flags']['noind']) {
905 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
906 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
907
                }
908
            } else {
909 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
910
            }
911 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
912
        }
913
914
        // Handle space control.
915 735
        if ($token[Token::POS_LSPACECTL]) {
916 30
            $token[Token::POS_LSPACE] = '';
917
        }
918 735
        if ($token[Token::POS_RSPACECTL]) {
919 33
            $token[Token::POS_RSPACE] = '';
920
        }
921 735
    }
922
}
923
924