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