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 ( f4df6d...71130e )
by Zordius
02:25
created

Validator::pushLeft()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
ccs 4
cts 4
cp 1
rs 10
cc 1
eloc 3
nc 1
nop 1
crap 1
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 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 715
    public static function verify(&$context, $template) {
39 715
        $template = SafeString::stripExtendedComments($template);
40 715
        $context['level'] = 0;
41 715
        Parser::setDelimiter($context);
42
43 715
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 704
            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 702
            $context['tokens']['count']++;
56 702
            $V = static::token($matches, $context);
57 702
            static::pushLeft($context);
58 702
            if ($V) {
59 663
                if (is_array($V)) {
60 656
                    array_push($V, $matches, $context['tokens']['partialind']);
61
                }
62 663
                static::pushToken($context, $V);
63
            }
64 702
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
65
        }
66 715
        static::pushToken($context, $template);
67
68 715
        if ($context['level'] > 0) {
69 6
            array_pop($context['stack']);
70 6
            array_pop($context['stack']);
71 6
            $token = array_pop($context['stack']);
72 6
            $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : "{{#{$token}}}") . ' !!';
73
        }
74 715
    }
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 702
    protected static function pushLeft(&$context) {
82 702
        static::pushToken($context, $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]);
83 702
        $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = '';
84 702
    }
85
86
    /**
87
     * push a token into the stack when it is not empty string
88
     *
89
     * @param array<string,array|string|integer> $context Current context
90
     * @param string|array $token a parsed token or a string
91
     */
92 715
    protected static function pushToken(&$context, $token) {
93 715
        if ($token === '') {
94 582
            return;
95
        }
96 704
        if (is_string($token)) {
97 478
            if (is_string(end($context['parsed'][0]))) {
98 31
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
99 31
                return;
100
            }
101
        }
102 704
        $context['parsed'][0][] = $token;
103 704
    }
104
105
    /**
106
     * push current token into the section stack
107
     *
108
     * @param array<string,array|string|integer> $context Current context
109
     * @param string $operation operation string
110
     * @param array<boolean|integer|string|array> $vars parsed arguments list
111
     */
112 349
    protected static function pushStack(&$context, $operation, $vars) {
113 349
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
114 349
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
115 349
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
116 349
        $context['stack'][] = $operation;
117 349
        $context['level']++;
118 349
    }
119
120
    /**
121
     * Verify delimiters and operators
122
     *
123
     * @param string[] $token detected handlebars {{ }} token
124
     * @param array<string,array|string|integer> $context current compile context
125
     *
126
     * @return boolean|null Return true when invalid
127
     *
128
     * @expect null when input array_fill(0, 11, ''), array()
129
     * @expect null when input array(0, 0, 0, 0, 0, '{{', '#', '...', '}}'), array()
130
     * @expect true when input array(0, 0, 0, 0, 0, '{', '#', '...', '}'), array()
131
     */
132 703
    protected static function delimiter($token, &$context) {
133
        // {{ }}} or {{{ }} are invalid
1 ignored issue
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...
134 703
        if (strlen($token[Token::POS_BEGINRAW]) !== strlen($token[Token::POS_ENDRAW])) {
135 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 => '}')) . '?';
136 6
            return true;
137
        }
138
        // {{{# }}} or {{{! }}} or {{{/ }}} or {{{^ }}} are invalid.
139 697
        if ((strlen($token[Token::POS_BEGINRAW]) == 1) && $token[Token::POS_OP] && ($token[Token::POS_OP] !== '&')) {
140 5
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' ?';
141 5
            return true;
142
        }
143 693
    }
144
145
    /**
146
     * Verify operators
147
     *
148
     * @param string $operator the operator string
149
     * @param array<string,array|string|integer> $context current compile context
150
     * @param array<boolean|integer|string|array> $vars parsed arguments list
151
     *
152
     * @return boolean|integer|null Return true when invalid or detected
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null|integer|double?

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...
153
     *
154
     * @expect null when input '', array(), array()
155
     * @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('foo'))
156
     * @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())
157
     * @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)), array(array('x'))
158
     * @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)), array(array('if'))
159
     * @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)), array(array('with'))
160
     * @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)), array(array('each'))
161
     * @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)), array(array('unless'))
162
     * @expect 9 when input '#', array('blockhelpers' => array('abc' => ''), 'usedFeature' => array('bhelper' => 8), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('abc'))
163
     * @expect 11 when input '#', array('hbhelpers' => array('abc' => ''), 'usedFeature' => array('hbhelper' => 10), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('abc'))
164
     * @expect true when input '>', array('basedir' => array('.'), 'fileext' => array('.tmpl'), 'usedFeature' => array('partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0)), array('test')
165
     */
166 664
    protected static function operator($operator, &$context, &$vars) {
167
        switch ($operator) {
168 664
            case '#*':
169 10
                static::pushLeft($context);
170 10
                $context['stack'][] = count($context['parsed'][0]);
171 10
                static::pushStack($context, '#*', $vars);
172 10
                array_unshift($context['inlinepartial'], '');
173 10
                return static::inline($context, $vars);
174
175
            case '#>':
176 13
                static::pushLeft($context);
177 13
                $context['stack'][] = count($context['parsed'][0]);
178 13
                static::pushStack($context, '#>', $vars);
179 13
                array_unshift($context['partialblock'], '');
180 13
                return static::partial($context, $vars);
181
182
            case '>':
183 88
                return static::partial($context, $vars);
184
185
            case '^':
186 66
                if (!isset($vars[0][0])) {
187 24
                    if (!$context['flags']['else']) {
188 1
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
189 1
                        return;
190
                    } else {
191 23
                        return static::doElse($context);
192
                    }
193
                }
194
195 42
                if (static::isBlockHelper($context, $vars)) {
196 3
                    static::pushStack($context, '#', $vars);
197 3
                    return static::blockCustomHelper($context, $vars, true);
198
                }
199
200 39
                static::pushStack($context, '^', $vars);
201 39
                return static::invertedSection($context, $vars);
202
203
            case '/':
204 332
                $r = static::blockEnd($context, $vars);
205 332
                array_pop($context['stack']);
206 332
                array_pop($context['stack']);
207 332
                array_pop($context['stack']);
208 332
                return $r;
209
210 504
            case '#':
211 305
                static::pushStack($context, '#', $vars);
212
213 305
                if (static::isBlockHelper($context, $vars)) {
214 59
                    return static::blockCustomHelper($context, $vars);
215
                }
216
217 253
                return static::blockBegin($context, $vars);
218
        }
219 504
    }
220
221
    /**
222
     * validate inline partial begin token
223
     *
224
     * @param array<string,array|string|integer> $context current compile context
225
     * @param array<boolean|integer|string|array> $vars parsed arguments list
226
     *
227
     * @return boolean|null Return true when inline partial ends
228
     */
229 663
    protected static function inlinePartial(&$context, $vars) {
230 663
        if (count($context['inlinepartial']) > 0) {
231 10
            $ended = false;
232 10
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
233
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
234 10
                $pb .= $append;
235 10
            });
236 10
            if ($context['currentToken'][Token::POS_OP] === '/') {
237 10
                if (static::blockEnd($context, $vars, '#*') !== null) {
238 10
                    $context['usedFeature']['inlpartial']++;
239 10
                    $tmpl = array_shift($context['inlinepartial']);
240 10
                    $c = $context['stack'][count($context['stack']) - 4];
241 10
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
242 10
                    $P = &$context['parsed'][0][$c];
243 10
                    $context['usedPartial'][$P[1][1][0]] = $tmpl;
244 10
                    $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]);
245 10
                    $ended = true;
246
                }
247
            }
248 10
            $append = Token::toString($context['currentToken']);
249
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
250 2
                $pb .= $append;
251 10
            });
252 10
            return $ended;
253
        }
254 663
    }
255
256
    /**
257
     * validate partial block token
258
     *
259
     * @param array<string,array|string|integer> $context current compile context
260
     * @param array<boolean|integer|string|array> $vars parsed arguments list
261
     *
262
     * @return boolean|null Return true when partial block ends
263
     */
264 663
    protected static function partialBlock(&$context, $vars) {
265 663
        if (count($context['partialblock']) > 0) {
266 13
            $ended = false;
267 13
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
268
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
269 13
                $pb .= $append;
270 13
            });
271 13
            if ($context['currentToken'][Token::POS_OP] === '/') {
272 13
                if (static::blockEnd($context, $vars, '#>') !== null) {
273 13
                    $c = $context['stack'][count($context['stack']) - 4];
274 13
                    $found = Partial::resolve($context, $vars[0][0]) !== null;
275 13
                    $v = $found ? '@partial-block' : $vars[0][0];
276 13
                    $context['usedPartial'][$v] = $context['partialblock'][0];
277 13
                    Partial::compileDynamic($context, $v);
278 13
                    if ($found) {
279 9
                        Partial::read($context, $vars[0][0]);
280
                    }
281 13
                    array_shift($context['partialblock']);
282 13
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
283 13
                    $ended = true;
284
                }
285
            }
286 13
            $append = Token::toString($context['currentToken']);
287 13
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
288 8
                $pb .= $append;
289 13
            });
290 13
            return $ended;
291
        }
292 663
    }
293
294
    /**
295
     * validate block begin token
296
     *
297
     * @param array<string,array|string|integer> $context current compile context
298
     * @param array<boolean|integer|string|array> $vars parsed arguments list
299
     *
300
     * @return boolean Return true always
301
     */
302 252
    protected static function blockBegin(&$context, $vars) {
303 252
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
304 5
            case 'with':
305 31
                return static::with($context, $vars);
306 5
            case 'each':
307 53
                return static::section($context, $vars, true);
308 5
            case 'unless':
309 6
                return static::unless($context, $vars);
310 115
            case 'if':
311 63
                return static::doIf($context, $vars);
312
            default:
313 115
                return static::section($context, $vars);
314
        }
315
    }
316
317
    /**
318
     * validate builtin helpers
319
     *
320
     * @param array<string,array|string|integer> $context current compile context
321
     * @param array<boolean|integer|string|array> $vars parsed arguments list
322
     */
323 139
    protected static function builtin(&$context, $vars) {
324 139
        if ($context['flags']['nohbh']) {
325 8
            if (isset($vars[1][0])) {
326 8
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
327
            }
328
        } else {
329 131
            if (count($vars) < 2) {
330 5
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
331
            }
332
        }
333 139
        $context['usedFeature'][$vars[0][0]]++;
334 139
    }
335
336
    /**
337
     * validate section 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
     * @param boolean $isEach the section is #each
342
     *
343
     * @return boolean Return true always
344
     */
345 168
    protected static function section(&$context, $vars, $isEach = false) {
346 168
        if ($isEach) {
347 53
            static::builtin($context, $vars);
348
        } else {
349 115
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
350 1
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
351
            }
352 115
            $context['usedFeature']['sec']++;
353
        }
354 168
        return true;
355
    }
356
357
    /**
358
     * validate with token
359
     *
360
     * @param array<string,array|string|integer> $context current compile context
361
     * @param array<boolean|integer|string|array> $vars parsed arguments list
362
     *
363
     * @return boolean Return true always
364
     */
365 31
    protected static function with(&$context, $vars) {
366 31
        static::builtin($context, $vars);
367 31
        return true;
368
    }
369
370
    /**
371
     * validate unless token
372
     *
373
     * @param array<string,array|string|integer> $context current compile context
374
     * @param array<boolean|integer|string|array> $vars parsed arguments list
375
     *
376
     * @return boolean Return true always
377
     */
378 6
    protected static function unless(&$context, $vars) {
379 6
        static::builtin($context, $vars);
380 6
        return true;
381
    }
382
383
    /**
384
     * validate if token
385
     *
386
     * @param array<string,array|string|integer> $context current compile context
387
     * @param array<boolean|integer|string|array> $vars parsed arguments list
388
     *
389
     * @return boolean Return true always
390
     */
391 63
    protected static function doIf(&$context, $vars) {
392 63
        static::builtin($context, $vars);
393 63
        return true;
394
    }
395
396
    /**
397
     * validate block custom helper token
398
     *
399
     * @param array<string,array|string|integer> $context current compile context
400
     * @param array<boolean|integer|string|array> $vars parsed arguments list
401
     * @param boolean $inverted the logic will be inverted
402
     *
403
     * @return string|null Return compiled code segment for the token
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|null?

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...
404
     */
405 61
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
406 61
        if (is_string($vars[0][0])) {
407
            // detect handlebars custom helpers.
408 61
            if (isset($context['hbhelpers'][$vars[0][0]])) {
409 58
                return ++$context['usedFeature']['hbhelper'];
410
            }
411
412
            // detect block custom helpers.
413 3
            if (isset($context['blockhelpers'][$vars[0][0]])) {
414 3
                return ++$context['usedFeature']['bhelper'];
415
            }
416
        }
417
    }
418
419
    /**
420
     * validate inverted section
421
     *
422
     * @param array<string,array|string|integer> $context current compile context
423
     * @param array<boolean|integer|string|array> $vars parsed arguments list
424
     *
425
     * @return integer Return number of inverted sections
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double?

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...
426
     */
427 38
    protected static function invertedSection(&$context, $vars) {
428 38
        return ++$context['usedFeature']['isec'];
429
    }
430
431
    /**
432
     * Return compiled PHP code for a handlebars block end token
433
     *
434
     * @param array<string,array|string|integer> $context current compile context
435
     * @param array<boolean|integer|string|array> $vars parsed arguments list
436
     * @param string|null $matchop should also match to this operator
0 ignored issues
show
Bug introduced by
There is no parameter named $matchop. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
437
     *
438
     * @return boolean Return true
439
     */
440 344
    protected static function blockEnd(&$context, $vars, $match = null) {
441 344
        $context['level']--;
442 344
        $c = count($context['stack']) - 2;
443 344
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
444 344
        if (($match !== null) && ($match !== $pop)) {
445 3
            return;
446
        }
447 344
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
448 344
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
449 325
            case 'with':
450 35
                if (!$context['flags']['nohbh']) {
451 33
                    if ($pop2 !== '[with]') {
452 1
                        $context['error'][] = 'Unexpect token: {{/with}} !';
453 1
                        return;
454
                    }
455
                }
456 34
                return true;
457
        }
458
459
        switch($pop) {
460 325
            case '#>':
461
            case '#*':
462
            case '#':
463 1
            case '^':
464 324
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
465 324
                $v = Expression::toString($levels, $spvar, $var);
466 324
                if ($pop2 !== $v) {
467 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
468 2
                    return;
469
                }
470 322
                if ($pop == '^') {
471 38
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
472
                }
473 292
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
474
            default:
475 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
476 1
                return;
477
        }
478
    }
479
480
    /**
481
     * handle delimiter change
482
     *
483
     * @param array<string,array|string|integer> $context current compile context
484
     *
485
     * @return boolean|null Return true when delimiter changed
486
     */
487 692
    protected static function isDelimiter(&$context) {
488 692
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
489 15
            $context['usedFeature']['delimiter']++;
490 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
491 15
            return true;
492
        }
493 683
    }
494
495
    /**
496
     * handle raw block
497
     *
498
     * @param string[] $token detected handlebars {{ }} token
499
     * @param array<string,array|string|integer> $context current compile context
500
     *
501
     * @return boolean|null Return true when in rawblock mode
502
     */
503 702
    protected static function rawblock(&$token, &$context) {
504 702
        $inner = $token[Token::POS_INNERTAG];
505 702
        trim($inner);
506
507
        // skip parse when inside raw block
508 702
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
509 4
            return true;
510
        }
511
512 702
        $token[Token::POS_INNERTAG] = $inner;
513
514
        // Handle raw block
515 702
        if ($token[Token::POS_BEGINRAW] === '{{') {
516 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
517 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
518
            }
519 7
            if ($context['rawblock']) {
520 4
                Parser::setDelimiter($context);
521 4
                $context['rawblock'] = false;
522
            } else {
523 7
                if ($token[Token::POS_OP]) {
524 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
525
                }
526 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
527 7
                Parser::setDelimiter($context);
528 7
                $token[Token::POS_OP] = '#';
529
            }
530 7
            $token[Token::POS_ENDRAW] = '}}';
531
        }
532 702
    }
533
534
    /**
535
     * handle comment
536
     *
537
     * @param string[] $token detected handlebars {{ }} token
538
     * @param array<string,array|string|integer> $context current compile context
539
     *
540
     * @return boolean|null Return true when is comment
541
     */
542 683
    protected static function comment(&$token, &$context) {
543 683
        if ($token[Token::POS_OP] === '!') {
544 25
            $context['usedFeature']['comment']++;
545 25
            return true;
546
        }
547 663
    }
548
549
    /**
550
     * Collect handlebars usage information, detect template error.
551
     *
552
     * @param string[] $token detected handlebars {{ }} token
553
     * @param array<string,array|string|integer> $context current compile context
554
     */
555 702
    protected static function token(&$token, &$context) {
556 702
        $context['currentToken'] = &$token;
557
558 702
        if (static::rawblock($token, $context)) {
559 4
            return Token::toString($token);
560
        }
561
562 702
        if (static::delimiter($token, $context)) {
563 10
            return;
564
        }
565
566 692
        if (static::isDelimiter($context)) {
567 15
            static::spacing($token, $context);
568 15
            return;
569
        }
570
571 683
        if (static::comment($token, $context)) {
572 25
            static::spacing($token, $context);
573 25
            return;
574
        }
575
576 663
        list($raw, $vars) = Parser::parse($token, $context);
577
578 663
        $partials = static::partialBlock($context, $vars);
579 663
        $partials = static::inlinePartial($context, $vars) || $partials;
580
581 663
        if ($partials) {
582 20
            $context['stack'] = array_slice($context['stack'], 0, -4);
583 20
            $context['currentToken'][Token::POS_LOTHER] = '';
584 20
            $context['currentToken'][Token::POS_LSPACE] = '';
585 20
            return;
586
        }
587
588
        // Handle spacing (standalone tags, partial indent)
589 663
        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));
590
591 663
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
592 397
            return array($raw, $vars);
593
        }
594
595 516
        if (count($vars) == 0) {
596 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
597
        }
598
599 510
        if (!isset($vars[0])) {
600 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
601
        }
602
603 509
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
604
605 509
        foreach ($vars as $var) {
606 509
            if (!isset($var[0]) || ($var[0] === 0)) {
607 66
                if ($context['level'] == 0) {
608 22
                    $context['usedFeature']['rootthis']++;
609
                }
610 509
                $context['usedFeature']['this']++;
611
            }
612
        }
613
614 509
        if (!isset($vars[0][0])) {
615 49
            return array($raw, $vars);
616
        }
617
618 477
        if (!static::helper($context, $vars[0][0])) {
619 368
            static::lookup($context, $vars);
620
        }
621
622 477
        return array($raw, $vars);
623
    }
624
625
    /**
626
     * Return 1 or larger number when else token detected
627
     *
628
     * @param array<string,array|string|integer> $context current compile context
629
     *
630
     * @return integer Return 1 or larger number when else token detected
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double?

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...
631
     */
632 23
    protected static function doElse(&$context) {
633 23
        if ($context['level'] == 0) {
634
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
635
        }
636 23
        return ++$context['usedFeature']['else'];
637
    }
638
639
    /**
640
     * Return true whe the name is listed in helper table
641
     *
642
     * @param array<string,array|string|integer> $context current compile context
643
     * @param array<boolean|integer|string|array> $vars parsed arguments list
644
     *
645
     * @return boolean Return true when it is custom helper
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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...
646
     */
647 368
    public static function lookup(&$context, $vars) {
648 368
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
649 4
            if (!$context['flags']['nohbh']) {
650 4
                if (count($vars) < 2) {
651 1
                    $context['error'][] = "No argument after {{lookup}} !";
652 3
                } else if (count($vars) < 3) {
653 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
654
                }
655 4
                $context['usedFeature']['lookup']++;
656 4
                return true;
657
            }
658
        }
659 364
    }
660
661
    /**
662
     * Return true when the name is listed in helper table
663
     *
664
     * @param array<string,array|string|integer> $context current compile context
665
     * @param string $name token name
666
     *
667
     * @return boolean Return true when it is custom helper
668
     */
669 485
    public static function helper(&$context, $name) {
670 485
        if (isset($context['hbhelpers'][$name])) {
671 102
            $context['usedFeature']['hbhelper']++;
672 102
            return true;
673
        }
674
675 391
        if (isset($context['helpers'][$name])) {
676 23
            $context['usedFeature']['helper']++;
677 23
            return true;
678
        }
679
680 370
        return false;
681
    }
682
683
    /**
684
     * detect for block custom helper
685
     *
686
     * @param array<string,array|string|integer> $context current compile context
687
     * @param array<boolean|integer|string|array> $vars parsed arguments list
688
     *
689
     * @return boolean|null Return true when this token is block custom helper
690
     */
691 336
    protected static function isBlockHelper($context, $vars) {
692 336
        if (!isset($vars[0][0])) {
693 4
            return;
694
        }
695
696 333
        if (!isset($context['blockhelpers'][$vars[0][0]]) && !isset($context['hbhelpers'][$vars[0][0]])) {
697 278
            return;
698
        }
699
700 61
        return true;
701
    }
702
703
    /**
704
     * validate inline partial
705
     *
706
     * @param array<string,array|string|integer> $context current compile context
707
     * @param array<boolean|integer|string|array> $vars parsed arguments list
708
     *
709
     * @return boolean Return true always
1 ignored issue
show
Documentation introduced by
Should the return type not be boolean|null?

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...
710
     */
711 10
    protected static function inline(&$context, $vars) {
712 10
        if (!$context['flags']['runpart']) {
713
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
714
        }
715 10
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
716
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
717
        }
718 10
        if (!isset($vars[1][0])) {
719
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
720
        }
721 10
    }
722
723
    /**
724
     * validate partial
725
     *
726
     * @param array<string,array|string|integer> $context current compile context
727
     * @param array<boolean|integer|string|array> $vars parsed arguments list
728
     *
729
     * @return integer|true Return 1 or larger number for runtime partial, return true for other case
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|boolean?

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...
730
     */
731 92
    protected static function partial(&$context, $vars) {
732 92
        if (Parser::isSubExp($vars[0])) {
733 5
            if ($context['flags']['runpart']) {
734 4
                return $context['usedFeature']['dynpartial']++;
735
            } else {
736 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
737 1
                return true;
738
            }
739
        } else {
740 87
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
741 82
                Partial::read($context, $vars[0][0]);
742
            }
743
        }
744 87
        if (!$context['flags']['runpart']) {
745 9
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
746 9
            if ($named || (count($vars) > 1)) {
747 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
748
            }
749
        }
750
751 87
        return true;
752
    }
753
754
    /**
755
     * Modify $token when spacing rules matched.
756
     *
757
     * @param array<string> $token detected handlebars {{ }} token
758
     * @param array<string,array|string|integer> $context current compile context
759
     * @param boolean $nost do not do stand alone logic
760
     *
761
     * @return string|null Return compiled code segment for the token
762
     */
763 692
    protected static function spacing(&$token, &$context, $nost = false) {
764
        // left line change detection
765 692
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
766 692
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
767
        // right line change detection
768 692
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
769 692
        $st = true;
770
        // setup ahead flag
771 692
        $ahead = $context['tokens']['ahead'];
772 692
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
773
        // reset partial indent
774 692
        $context['tokens']['partialind'] = '';
775
        // same tags in the same line , not standalone
776 692
        if (!$lsp && $ahead) {
777 338
            $st = false;
778
        }
779 692
        if ($nost) {
780 495
            $st = false;
781
        }
782
        // not standalone because other things in the same line ahead
783 692
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
784 196
            $st = false;
785
        }
786
        // not standalone because other things in the same line behind
787 692
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
788 337
            $st = false;
789
        }
790 692
        if ($st && (($lsp && $rsp) // both side cr
791 123
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
792 692
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
793
           )) {
794
            // handle partial
795 63
            if ($token[Token::POS_OP] === '>') {
796 15
                if (!$context['flags']['noind']) {
797 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
798 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
799
                }
800
            } else {
801 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
802
            }
803 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
804
        }
805
806
        // Handle space control.
807 692
        if ($token[Token::POS_LSPACECTL]) {
808 30
            $token[Token::POS_LSPACE] = '';
809
        }
810 692
        if ($token[Token::POS_RSPACECTL]) {
811 33
            $token[Token::POS_RSPACE] = '';
812
        }
813 692
    }
814
}
815
816