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 ( 428a72...2ffdde )
by Zordius
03:04
created

Validator::partial()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 6.9811
cc 7
eloc 15
nc 8
nop 2
crap 7
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 729
    public static function verify(&$context, $template) {
39 729
        $template = SafeString::stripExtendedComments($template);
40 729
        $context['level'] = 0;
41 729
        Parser::setDelimiter($context);
42
43 729
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 718
            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 716
            $context['tokens']['count']++;
56 716
            $V = static::token($matches, $context);
57 716
            static::pushLeft($context);
58 716
            if ($V) {
59 677
                if (is_array($V)) {
60 670
                    array_push($V, $matches, $context['tokens']['partialind']);
61
                }
62 677
                static::pushToken($context, $V);
63
            }
64 716
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
65
        }
66 729
        static::pushToken($context, $template);
67
68 729
        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 729
    }
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 716
    protected static function pushLeft(&$context) {
82 716
        static::pushToken($context, $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]);
83 716
        $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = '';
84 716
    }
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 729
    protected static function pushToken(&$context, $token) {
93 729
        if ($token === '') {
94 596
            return;
95
        }
96 718
        if (is_string($token)) {
97 482
            if (is_string(end($context['parsed'][0]))) {
98 31
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
99 31
                return;
100
            }
101
        }
102 718
        $context['parsed'][0][] = $token;
103 718
    }
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 355
    protected static function pushStack(&$context, $operation, $vars) {
113 355
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
114 355
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
115 355
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
116 355
        $context['stack'][] = $operation;
117 355
        $context['level']++;
118 355
    }
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 717
    protected static function delimiter($token, &$context) {
133
        // {{ }}} or {{{ }} are invalid
134 717
        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 711
        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 707
    }
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
1 ignored issue
show
Documentation introduced by
Should the return type not be boolean|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...
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), 'helperresolver' => 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), 'elseif' => false, 'elselvl' => array(), 'helperresolver' => 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), 'elseif' => false, 'elselvl' => array(), 'helperresolver' => 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), 'elseif' => false, 'elselvl' => array(), 'helperresolver' => 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), 'elseif' => false, 'elselvl' => array(), 'helperresolver' => 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), 'elseif' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('unless'))
162
     * @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), 'elseif' => false, 'elselvl' => array()), array(array('abc'))
163
     * @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), 'elseif' => false, 'elselvl' => array()), array(array('abc'))
164
     * @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), 'elseif' => false, 'elselvl' => array()), array('test')
165
     */
166 678
    protected static function operator($operator, &$context, &$vars) {
167
        switch ($operator) {
168 678
            case '#*':
169 13
                if (!$context['compile']) {
170 13
                    static::pushLeft($context);
171 13
                    $context['stack'][] = count($context['parsed'][0]);
172 13
                    static::pushStack($context, '#*', $vars);
173 13
                    array_unshift($context['inlinepartial'], '');
174
                }
175 13
                return static::inline($context, $vars);
176
177
            case '#>':
178 14
                if (!$context['compile']) {
179 14
                    static::pushLeft($context);
180 14
                    $context['stack'][] = count($context['parsed'][0]);
181 14
                    $vars[Parser::PARTIALBLOCK] = ++$context['usedFeature']['pblock'];
182 14
                    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...
183 14
                    array_unshift($context['partialblock'], '');
184
                }
185 14
                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...
186
187
            case '>':
188 91
                return static::partial($context, $vars);
189
190
            case '^':
191 64
                if (!isset($vars[0][0])) {
192 24
                    if (!$context['flags']['else']) {
193 1
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
194 1
                        return;
195
                    } else {
196 23
                        return static::doElse($context, $vars);
197
                    }
198
                }
199
200 40
                if (static::isBlockHelper($context, $vars)) {
201 1
                    static::pushStack($context, '#', $vars);
202 1
                    return static::blockCustomHelper($context, $vars, true);
203
                }
204
205 39
                static::pushStack($context, '^', $vars);
206 39
                return static::invertedSection($context, $vars);
207
208
            case '/':
209 334
                $r = static::blockEnd($context, $vars);
210 334
                array_pop($context['stack']);
211 334
                array_pop($context['stack']);
212 334
                array_pop($context['stack']);
213 334
                return $r;
214
215 513
            case '#':
216 309
                if (static::isBlockHelper($context, $vars)) {
217 59
                    static::pushStack($context, '#', $vars);
218 59
                    return static::blockCustomHelper($context, $vars);
219
                }
220
221 257
                if (!$context['elseif']) {
222 257
                    static::pushStack($context, '#', $vars);
223
                }
224
225 257
                return static::blockBegin($context, $vars);
226
        }
227 513
    }
228
229
    /**
230
     * validate inline partial begin token
231
     *
232
     * @param array<string,array|string|integer> $context current compile context
233
     * @param array<boolean|integer|string|array> $vars parsed arguments list
234
     *
235
     * @return boolean|null Return true when inline partial ends
236
     */
237 677
    protected static function inlinePartial(&$context, $vars) {
238 677
        if (count($context['inlinepartial']) > 0) {
239 13
            $ended = false;
240 13
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
241
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
242 13
                $pb .= $append;
243 13
            });
244 13
            if ($context['currentToken'][Token::POS_OP] === '/') {
245 13
                if (static::blockEnd($context, $vars, '#*') !== null) {
246 13
                    $context['usedFeature']['inlpartial']++;
247 13
                    $tmpl = array_shift($context['inlinepartial']);
248 13
                    $c = $context['stack'][count($context['stack']) - 4];
249 13
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
250 13
                    $P = &$context['parsed'][0][$c];
251 13
                    if (isset($P[1][1][0])) {
252 12
                        $context['usedPartial'][$P[1][1][0]] = $tmpl;
253 12
                        $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]);
254
                    }
255 13
                    $ended = true;
256
                }
257
            }
258 13
            $append = Token::toString($context['currentToken']);
259
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
260 2
                $pb .= $append;
261 13
            });
262 13
            return $ended;
263
        }
264 677
    }
265
266
    /**
267
     * validate partial block token
268
     *
269
     * @param array<string,array|string|integer> $context current compile context
270
     * @param array<boolean|integer|string|array> $vars parsed arguments list
271
     *
272
     * @return boolean|null Return true when partial block ends
273
     */
274 677
    protected static function partialBlock(&$context, $vars) {
275 677
        if (count($context['partialblock']) > 0) {
276 14
            $ended = false;
277 14
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
278
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
279 14
                $pb .= $append;
280 14
            });
281 14
            if ($context['currentToken'][Token::POS_OP] === '/') {
282 14
                if (static::blockEnd($context, $vars, '#>') !== null) {
283 14
                    $c = $context['stack'][count($context['stack']) - 4];
284 14
                    $found = Partial::resolve($context, $vars[0][0]) !== null;
285 14
                    $v = $found ? "@partial-block{$context['usedFeature']['pblock']}" : "{$vars[0][0]}";
286 14
                    if ($found) {
287 10
                        $context['partials'][$v] = $context['partialblock'][0];
288
                    }
289 14
                    $context['usedPartial'][$v] = $context['partialblock'][0];
290 14
                    Partial::compileDynamic($context, $v);
291 14
                    if ($found) {
292 10
                        Partial::read($context, $vars[0][0]);
293
                    }
294 14
                    array_shift($context['partialblock']);
295 14
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
296 14
                    $ended = true;
297
                }
298
            }
299 14
            $append = Token::toString($context['currentToken']);
300 14
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
301 8
                $pb .= $append;
302 14
            });
303 14
            return $ended;
304
        }
305 677
    }
306
307
    /**
308
     * handle else if
309
     *
310
     * @param array<string,array|string|integer> $context current compile context
311
     */
312 76
    protected static function doElseIf(&$context) {
313 76
        if ($context['elseif']) {
314 8
            $context['elseif'] = false;
315
        } else {
316 70
            array_unshift($context['elselvl'], 0);
317
        }
318 76
    }
319
320
    /**
321
     * validate block begin token
322
     *
323
     * @param array<string,array|string|integer> $context current compile context
324
     * @param array<boolean|integer|string|array> $vars parsed arguments list
325
     *
326
     * @return boolean Return true always
327
     */
328 256
    protected static function blockBegin(&$context, $vars) {
329 256
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
330 5
            case 'with':
331 30
                array_unshift($context['elselvl'], 0);
332 30
                return static::with($context, $vars);
333 5
            case 'each':
334 53
                array_unshift($context['elselvl'], 0);
335 53
                return static::section($context, $vars, true);
336 5
            case 'unless':
337 7
                return static::unless($context, $vars);
338 118
            case 'if':
339 70
                return static::doIf($context, $vars);
340
            default:
341 118
                array_unshift($context['elselvl'], 0);
342 118
                return static::section($context, $vars);
343
        }
344
    }
345
346
    /**
347
     * validate builtin helpers
348
     *
349
     * @param array<string,array|string|integer> $context current compile context
350
     * @param array<boolean|integer|string|array> $vars parsed arguments list
351
     */
352 146
    protected static function builtin(&$context, $vars) {
353 146
        if ($context['flags']['nohbh']) {
354 8
            if (isset($vars[1][0])) {
355 8
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
356
            }
357
        } else {
358 138
            if (count($vars) < 2) {
359 5
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
360
            }
361
        }
362 146
        $context['usedFeature'][$vars[0][0]]++;
363 146
    }
364
365
    /**
366
     * validate section token
367
     *
368
     * @param array<string,array|string|integer> $context current compile context
369
     * @param array<boolean|integer|string|array> $vars parsed arguments list
370
     * @param boolean $isEach the section is #each
371
     *
372
     * @return boolean Return true always
373
     */
374 171
    protected static function section(&$context, $vars, $isEach = false) {
375 171
        if ($isEach) {
376 53
            static::builtin($context, $vars);
377
        } else {
378 118
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
379 1
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
380
            }
381 118
            $context['usedFeature']['sec']++;
382
        }
383 171
        return true;
384
    }
385
386
    /**
387
     * validate with token
388
     *
389
     * @param array<string,array|string|integer> $context current compile context
390
     * @param array<boolean|integer|string|array> $vars parsed arguments list
391
     *
392
     * @return boolean Return true always
393
     */
394 30
    protected static function with(&$context, $vars) {
395 30
        static::builtin($context, $vars);
396 30
        return true;
397
    }
398
399
    /**
400
     * validate unless 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 7
    protected static function unless(&$context, $vars) {
408 7
        static::doElseIf($context);
409 7
        static::builtin($context, $vars);
410 7
        return true;
411
    }
412
413
    /**
414
     * validate if token
415
     *
416
     * @param array<string,array|string|integer> $context current compile context
417
     * @param array<boolean|integer|string|array> $vars parsed arguments list
418
     *
419
     * @return boolean Return true always
420
     */
421 70
    protected static function doIf(&$context, $vars) {
422 70
        static::doElseIf($context);
423 70
        static::builtin($context, $vars);
424 70
        return true;
425
    }
426
427
    /**
428
     * validate block custom helper token
429
     *
430
     * @param array<string,array|string|integer> $context current compile context
431
     * @param array<boolean|integer|string|array> $vars parsed arguments list
432
     * @param boolean $inverted the logic will be inverted
433
     *
434
     * @return integer|null Return number of used custom helpers
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...
435
     */
436 59
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
437 59
        if (is_string($vars[0][0])) {
438 59
            if (static::resolveHelper($context, $vars[0][0])) {
439 59
                return ++$context['usedFeature']['helper'];
440
            }
441
        }
442
    }
443
444
    /**
445
     * validate inverted section
446
     *
447
     * @param array<string,array|string|integer> $context current compile context
448
     * @param array<boolean|integer|string|array> $vars parsed arguments list
449
     *
450
     * @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...
451
     */
452 38
    protected static function invertedSection(&$context, $vars) {
453 38
        return ++$context['usedFeature']['isec'];
454
    }
455
456
    /**
457
     * Return compiled PHP code for a handlebars block end token
458
     *
459
     * @param array<string,array|string|integer> $context current compile context
460
     * @param array<boolean|integer|string|array> $vars parsed arguments list
461
     * @param string|null $match should also match to this operator
462
     *
463
     * @return boolean Return true
0 ignored issues
show
Documentation introduced by
Should the return type not be null|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...
464
     */
465 350
    protected static function blockEnd(&$context, &$vars, $match = null) {
466 350
        $context['level']--;
467 350
        $c = count($context['stack']) - 2;
468 350
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
469 350
        if (($match !== null) && ($match !== $pop)) {
470 3
            return;
471
        }
472 350
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
473 350
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
474 331
            case 'with':
475 34
                if (!$context['flags']['nohbh']) {
476 32
                    if ($pop2 !== '[with]') {
477 1
                        $context['error'][] = 'Unexpect token: {{/with}} !';
478 1
                        return;
479
                    }
480
                }
481 33
                return true;
482
        }
483
484
        switch($pop) {
485 331
            case '#>':
486
            case '#*':
487
            case '#':
488 1
            case '^':
489 330
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
490 330
                $v = Expression::toString($levels, $spvar, $var);
491 330
                if ($pop2 !== $v) {
492 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
493 2
                    return;
494
                }
495 328
                if (count($context['elselvl']) > 0) {
496 229
                    $vars[0][-1] = $context['elselvl'][0];
497 229
                    array_shift($context['elselvl']);
498
                }
499 328
                return true;
500
            default:
501 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
502 1
                return;
503
        }
504
    }
505
506
    /**
507
     * handle delimiter change
508
     *
509
     * @param array<string,array|string|integer> $context current compile context
510
     *
511
     * @return boolean|null Return true when delimiter changed
512
     */
513 706
    protected static function isDelimiter(&$context) {
514 706
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
515 15
            $context['usedFeature']['delimiter']++;
516 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
517 15
            return true;
518
        }
519 697
    }
520
521
    /**
522
     * handle raw block
523
     *
524
     * @param string[] $token detected handlebars {{ }} token
525
     * @param array<string,array|string|integer> $context current compile context
526
     *
527
     * @return boolean|null Return true when in rawblock mode
528
     */
529 716
    protected static function rawblock(&$token, &$context) {
530 716
        $inner = $token[Token::POS_INNERTAG];
531 716
        trim($inner);
532
533
        // skip parse when inside raw block
534 716
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
535 4
            return true;
536
        }
537
538 716
        $token[Token::POS_INNERTAG] = $inner;
539
540
        // Handle raw block
541 716
        if ($token[Token::POS_BEGINRAW] === '{{') {
542 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
543 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
544
            }
545 7
            if ($context['rawblock']) {
546 4
                Parser::setDelimiter($context);
547 4
                $context['rawblock'] = false;
548
            } else {
549 7
                if ($token[Token::POS_OP]) {
550 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
551
                }
552 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
553 7
                Parser::setDelimiter($context);
554 7
                $token[Token::POS_OP] = '#';
555
            }
556 7
            $token[Token::POS_ENDRAW] = '}}';
557
        }
558 716
    }
559
560
    /**
561
     * handle comment
562
     *
563
     * @param string[] $token detected handlebars {{ }} token
564
     * @param array<string,array|string|integer> $context current compile context
565
     *
566
     * @return boolean|null Return true when is comment
567
     */
568 697
    protected static function comment(&$token, &$context) {
569 697
        if ($token[Token::POS_OP] === '!') {
570 25
            $context['usedFeature']['comment']++;
571 25
            return true;
572
        }
573 677
    }
574
575
    /**
576
     * Collect handlebars usage information, detect template error.
577
     *
578
     * @param string[] $token detected handlebars {{ }} token
579
     * @param array<string,array|string|integer> $context current compile context
580
     */
581 716
    protected static function token(&$token, &$context) {
582 716
        $context['currentToken'] = &$token;
583
584 716
        if (static::rawblock($token, $context)) {
585 4
            return Token::toString($token);
586
        }
587
588 716
        if (static::delimiter($token, $context)) {
589 10
            return;
590
        }
591
592 706
        if (static::isDelimiter($context)) {
593 15
            static::spacing($token, $context);
594 15
            return;
595
        }
596
597 697
        if (static::comment($token, $context)) {
598 25
            static::spacing($token, $context);
599 25
            return;
600
        }
601
602 677
        list($raw, $vars) = Parser::parse($token, $context);
603
604 677
        $partials = static::partialBlock($context, $vars);
605 677
        $partials = static::inlinePartial($context, $vars) || $partials;
606
607 677
        if ($partials) {
608 24
            $context['stack'] = array_slice($context['stack'], 0, -4);
609 24
            $context['currentToken'][Token::POS_LOTHER] = '';
610 24
            $context['currentToken'][Token::POS_LSPACE] = '';
611 24
            return;
612
        }
613
614
        // Handle spacing (standalone tags, partial indent)
615 677
        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));
616
617 677
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
618 406
            return array($raw, $vars);
619
        }
620
621 515
        if (count($vars) == 0) {
622 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
623
        }
624
625 509
        if (!isset($vars[0])) {
626 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
627
        }
628
629 508
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
630
631 508
        foreach ($vars as $var) {
632 508
            if (!isset($var[0]) || ($var[0] === 0)) {
633 64
                if ($context['level'] == 0) {
634 22
                    $context['usedFeature']['rootthis']++;
635
                }
636 508
                $context['usedFeature']['this']++;
637
            }
638
        }
639
640 508
        if (!isset($vars[0][0])) {
641 47
            return array($raw, $vars);
642
        }
643
644 476
        if ($vars[0][0] === 'else') {
645 28
            static::doElse($context, $vars);
646 28
            return array($raw, $vars);
647
        }
648
649 460
        if (!static::helper($context, $vars[0][0])) {
650 350
            static::lookup($context, $vars);
651 350
            static::log($context, $vars);
652
        }
653
654 460
        return array($raw, $vars);
655
    }
656
657
    /**
658
     * Return 1 or larger number when else token detected
659
     *
660
     * @param array<string,array|string|integer> $context current compile context
661
     * @param array<boolean|integer|string|array> $vars parsed arguments list
662
     *
663
     * @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...
664
     */
665 50
    protected static function doElse(&$context, $vars) {
666 50
        if ($context['level'] == 0) {
667 1
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
668
        }
669
670 50
        if (isset($vars[1][0]) && (($vars[1][0] === 'if') || ($vars[1][0] === 'unless'))) {
671 8
            $token = $context['currentToken'];
672 8
            $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];
673 8
            $context['elseif'] = true;
674 8
            $context['elselvl'][0]++;
675
        }
676
677 50
        return ++$context['usedFeature']['else'];
678
    }
679
680
    /**
681
     * Return true when this is {{log ...}}
682
     *
683
     * @param array<string,array|string|integer> $context current compile context
684
     * @param array<boolean|integer|string|array> $vars parsed arguments list
685
     *
686
     * @return boolean|null Return true when it is custom helper
687
     */
688 350
    public static function log(&$context, $vars) {
689 350
        if (isset($vars[0][0]) && ($vars[0][0] === 'log')) {
690 3
            if (!$context['flags']['nohbh']) {
691 3
                if (count($vars) < 2) {
692 1
                    $context['error'][] = "No argument after {{log}} !";
693
                }
694 3
                $context['usedFeature']['log']++;
695 3
                return true;
696
            }
697
        }
698 347
    }
699
700
    /**
701
     * Return true when this is {{lookup ...}}
702
     *
703
     * @param array<string,array|string|integer> $context current compile context
704
     * @param array<boolean|integer|string|array> $vars parsed arguments list
705
     *
706
     * @return boolean|null Return true when it is custom helper
707
     */
708 350
    public static function lookup(&$context, $vars) {
709 350
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
710 4
            if (!$context['flags']['nohbh']) {
711 4
                if (count($vars) < 2) {
712 1
                    $context['error'][] = "No argument after {{lookup}} !";
713 3
                } else if (count($vars) < 3) {
714 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
715
                }
716 4
                $context['usedFeature']['lookup']++;
717 4
                return true;
718
            }
719
        }
720 346
    }
721
722
    /**
723
     * Return true when the name is listed in helper table
724
     *
725
     * @param array<string,array|string|integer> $context current compile context
726
     * @param string $name token name
727
     *
728
     * @return boolean Return true when it is custom helper
729
     */
730 468
    public static function helper(&$context, $name) {
731 468
        if (static::resolveHelper($context, $name)) {
732 125
            $context['usedFeature']['helper']++;
733 125
            return true;
734
        }
735
736 352
        return false;
737
    }
738
739
    /**
740
     * use helperresolver to resolve helper, return true when helper founded
741
     *
742
     * @param array<string,array|string|integer> $context Current context of compiler progress.
743
     * @param string $name helper name
744
     *
745
     * @return string|null $content helper function name or callable
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...
746
     */
747 619
    public static function resolveHelper(&$context, &$name) {
748 619
        if (isset($context['helpers'][$name])) {
749 173
            return true;
750
        }
751
752 498
        if ($context['helperresolver']) {
753 2
            $helper = $context['helperresolver']($context, $name);
754 2
            if ($helper) {
755 2
                $context['helpers'][$name] = $helper;
756 2
                return true;
757
            }
758
        }
759 496
    }
760
761
    /**
762
     * detect for block custom helper
763
     *
764
     * @param array<string,array|string|integer> $context current compile context
765
     * @param array<boolean|integer|string|array> $vars parsed arguments list
766
     *
767
     * @return boolean|null Return true when this token is block custom helper
768
     */
769 338
    protected static function isBlockHelper($context, $vars) {
770 338
        if (!isset($vars[0][0])) {
771 4
            return;
772
        }
773
774 335
        if (!static::resolveHelper($context, $vars[0][0])) {
1 ignored issue
show
Bug Best Practice introduced by
The expression static::resolveHelper($context, $vars[0][0]) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
775 282
            return;
776
        }
777
778 59
        return true;
779
    }
780
781
    /**
782
     * validate inline partial
783
     *
784
     * @param array<string,array|string|integer> $context current compile context
785
     * @param array<boolean|integer|string|array> $vars parsed arguments list
786
     *
787
     * @return boolean Return true always
788
     */
789 13
    protected static function inline(&$context, $vars) {
790 13
        if (!$context['flags']['runpart']) {
791 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
792
        }
793 13
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
794 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
795
        }
796 13
        if (!isset($vars[1][0])) {
797 1
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
798
        }
799 13
        return true;
800
    }
801
802
    /**
803
     * validate partial
804
     *
805
     * @param array<string,array|string|integer> $context current compile context
806
     * @param array<boolean|integer|string|array> $vars parsed arguments list
807
     *
808
     * @return integer|boolean 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...
809
     */
810 95
    protected static function partial(&$context, $vars) {
811 95
        if (Parser::isSubExp($vars[0])) {
812 5
            if ($context['flags']['runpart']) {
813 4
                return $context['usedFeature']['dynpartial']++;
814
            } else {
815 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
816 1
                return true;
817
            }
818
        } else {
819 90
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
820 85
                Partial::read($context, $vars[0][0]);
821
            }
822
        }
823 90
        if (!$context['flags']['runpart']) {
824 11
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
825 11
            if ($named || (count($vars) > 1)) {
826 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
827
            }
828
        }
829
830 90
        return true;
831
    }
832
833
    /**
834
     * Modify $token when spacing rules matched.
835
     *
836
     * @param array<string> $token detected handlebars {{ }} token
837
     * @param array<string,array|string|integer> $context current compile context
838
     * @param boolean $nost do not do stand alone logic
839
     *
840
     * @return string|null Return compiled code segment for the token
841
     */
842 706
    protected static function spacing(&$token, &$context, $nost = false) {
843
        // left line change detection
844 706
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
845 706
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
846
        // right line change detection
847 706
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
848 706
        $st = true;
849
        // setup ahead flag
850 706
        $ahead = $context['tokens']['ahead'];
851 706
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
852
        // reset partial indent
853 706
        $context['tokens']['partialind'] = '';
854
        // same tags in the same line , not standalone
855 706
        if (!$lsp && $ahead) {
856 344
            $st = false;
857
        }
858 706
        if ($nost) {
859 503
            $st = false;
860
        }
861
        // not standalone because other things in the same line ahead
862 706
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
863 198
            $st = false;
864
        }
865
        // not standalone because other things in the same line behind
866 706
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
867 346
            $st = false;
868
        }
869 706
        if ($st && (($lsp && $rsp) // both side cr
870 126
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
871 706
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
872
           )) {
873
            // handle partial
874 63
            if ($token[Token::POS_OP] === '>') {
875 15
                if (!$context['flags']['noind']) {
876 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
877 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
878
                }
879
            } else {
880 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
881
            }
882 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
883
        }
884
885
        // Handle space control.
886 706
        if ($token[Token::POS_LSPACECTL]) {
887 30
            $token[Token::POS_LSPACE] = '';
888
        }
889 706
        if ($token[Token::POS_RSPACECTL]) {
890 33
            $token[Token::POS_RSPACE] = '';
891
        }
892 706
    }
893
}
894
895