GitHub Access Token became invalid

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

Validator::partialBlock()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 25
ccs 20
cts 20
cp 1
rs 8.439
cc 5
eloc 20
nc 5
nop 3
crap 5
1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2015 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file to keep LightnCandy Validator
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
use \LightnCandy\Token;
23
use \LightnCandy\Parser;
24
use \LightnCandy\Partial;
25
use \LightnCandy\Expression;
26
use \LightnCandy\SafeString;
27
28
/**
29
 * LightnCandy Validator
30
 */
31
class Validator {
32
    /**
33
     * Verify template
34
     *
35
     * @param array<string,array|string|integer> $context Current context
36
     * @param string $template handlebars template
37
     */
38 702
    public static function verify(&$context, $template) {
39 702
        $template = SafeString::escapeTemplate(SafeString::stripExtendedComments($template));
40 702
        $context['level'] = 0;
41 702
        Parser::setDelimiter($context);
42
43 702
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 691
            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 689
            $context['tokens']['count']++;
56 689
            $V = static::token($matches, $context);
57 689
            static::pushToken($context, $matches[Token::POS_LOTHER]);
58 689
            static::pushToken($context, $matches[Token::POS_LSPACE]);
59 689
            if ($V) {
60 650
                if (is_array($V)) {
61 643
                    array_push($V, $matches, $context['tokens']['partialind']);
62
                }
63 650
                static::pushToken($context, $V);
64
            }
65 689
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
66
        }
67 702
        static::pushToken($context, $template);
68
69 702
        if ($context['level'] > 0) {
70 6
            array_pop($context['stack']);
71 6
            array_pop($context['stack']);
72 6
            $token = array_pop($context['stack']);
73 6
            $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : "{{#{$token}}}") . ' !!';
74
        }
75 702
    }
76
77
    /**
78
     * push a token into the stack when it is not empty string
79
     *
80
     * @param array<string,array|string|integer> $context Current context
81
     * @param string|array $token a parsed token or a string
82
     */
83 702
    protected static function pushToken(&$context, $token) {
84 702
        if ($token === '') {
85 647
            return;
86
        }
87 691
        if (is_string($token)) {
88 465
            if (is_string(end($context['parsed'][0]))) {
89 223
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
90 223
                return;
91
            }
92
        }
93 691
        $context['parsed'][0][] = $token;
94 691
    }
95
96
    /**
97
     * push current token into the section stack
98
     *
99
     * @param array<string,array|string|integer> $context Current context
100
     * @param string $operation operation string
101
     * @param array<boolean|integer|string|array> $vars parsed arguments list
102
     */
103 337
    protected static function pushStack(&$context, $operation, $vars) {
104 337
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
105 337
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
106 337
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
107 337
        $context['stack'][] = $operation;
108 337
        $context['level']++;
109 337
    }
110
111
    /**
112
     * Verify delimiters and operators
113
     *
114
     * @param string[] $token detected handlebars {{ }} token
115
     * @param array<string,array|string|integer> $context current compile context
116
     *
117
     * @return boolean|null Return true when invalid
118
     *
119
     * @expect null when input array_fill(0, 11, ''), array()
120
     * @expect null when input array(0, 0, 0, 0, 0, '{{', '#', '...', '}}'), array()
121
     * @expect true when input array(0, 0, 0, 0, 0, '{', '#', '...', '}'), array()
122
     */
123 690
    protected static function delimiter($token, &$context) {
124
        // {{ }}} 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...
125 690
        if (strlen($token[Token::POS_BEGINRAW]) !== strlen($token[Token::POS_ENDRAW])) {
126 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 => '}')) . '?';
127 6
            return true;
128
        }
129
        // {{{# }}} or {{{! }}} or {{{/ }}} or {{{^ }}} are invalid.
130 684
        if ((strlen($token[Token::POS_BEGINRAW]) == 1) && $token[Token::POS_OP] && ($token[Token::POS_OP] !== '&')) {
131 5
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' ?';
132 5
            return true;
133
        }
134 680
    }
135
136
    /**
137
     * Verify operators
138
     *
139
     * @param string $operator the operator string
140
     * @param array<string,array|string|integer> $context current compile context
141
     * @param array<boolean|integer|string|array> $vars parsed arguments list
142
     *
143
     * @return boolean|integer|null Return true when invalid or detected
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|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...
144
     *
145
     * @expect null when input '', array(), array()
146
     * @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('foo'))
147
     * @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())
148
     * @expect 4 when input '#', array('usedFeature' => array('sec' => 3), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('x'))
149
     * @expect 5 when input '#', array('usedFeature' => array('if' => 4), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0)), array(array('if'))
150
     * @expect 6 when input '#', array('usedFeature' => array('with' => 5), 'level' => 0, 'flags' => array('nohbh' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0)), array(array('with'))
151
     * @expect 7 when input '#', array('usedFeature' => array('each' => 6), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0)), array(array('each'))
152
     * @expect 8 when input '#', array('usedFeature' => array('unless' => 7), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0)), array(array('unless'))
153
     * @expect 9 when input '#', array('blockhelpers' => array('abc' => ''), 'usedFeature' => array('bhelper' => 8), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('abc'))
154
     * @expect 11 when input '#', array('hbhelpers' => array('abc' => ''), 'usedFeature' => array('hbhelper' => 10), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('abc'))
155
     * @expect true when input '>', array('basedir' => array('.'), 'fileext' => array('.tmpl'), 'usedFeature' => array('partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0)), array('test')
156
     */
157 651
    protected static function operator($operator, &$context, &$vars) {
158
        switch ($operator) {
159 651
            case '#>':
160 8
                static::pushStack($context, '#>', $vars);
161 8
                array_unshift($context['partialblock'], '');
162 8
                return static::partial($context, $vars);
163
164
            case '>':
165 72
                return static::partial($context, $vars);
166
167
            case '^':
168 66
                if (!isset($vars[0][0])) {
169 24
                    if (!$context['flags']['else']) {
170 1
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
171 1
                        return;
172
                    } else {
173 23
                        return static::doElse($context);
174
                    }
175
                }
176
177 42
                if (static::isBlockHelper($context, $vars)) {
178 3
                    static::pushStack($context, '#', $vars);
179 3
                    return static::blockCustomHelper($context, $vars, true);
180
                }
181
182 39
                static::pushStack($context, '^', $vars);
183 39
                return static::invertedSection($context, $vars);
184
185
            case '/':
186 327
                $r = static::blockEnd($context, $vars);
187 327
                array_pop($context['stack']);
188 327
                array_pop($context['stack']);
189 327
                array_pop($context['stack']);
190 327
                return $r;
191
192 501
            case '#':
193 300
                static::pushStack($context, '#', $vars);
194
195 300
                if (static::isBlockHelper($context, $vars)) {
196 59
                    return static::blockCustomHelper($context, $vars);
197
                }
198
199 248
                return static::blockBegin($context, $vars);
200
        }
201 501
    }
202
203
    /**
204
     * validate block begin token
205
     *
206
     * @param string $operator the operator string
207
     * @param array<string,array|string|integer> $context current compile context
208
     * @param array<boolean|integer|string|array> $vars parsed arguments list
209
     *
210
     * @return boolean Return true always
1 ignored issue
show
Documentation introduced by
Should the return type not be boolean|null?

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

Loading history...
211
     */
212 650
    protected static function partialBlock($operator, &$context, $vars) {
213 650
        if (count($context['partialblock']) > 0) {
214 8
            $context['partialblock'][0] .= $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
215 8
            if ($operator === '/') {
216 8
                if (static::blockEnd($context, $vars) !== null) {
217 8
                    if (Partial::resolvePartial($vars[0][0], $context) === null) {
218 3
                        $context['usedPartial'][$vars[0][0]] = $context['partialblock'][0];
219 3
                        $tmpPB = $context['partialblock'];
220 3
                        $context['partialblock'] = array();
221 3
                        Partial::compileDynamic($vars[0][0], $context);
222 3
                        $context['partialblock'] = $tmpPB;
223
                    } else {
224 5
                        Partial::readPartial($vars[0][0], $context);
225
                    }
226 8
                    array_pop($context['stack']);
227 8
                    array_pop($context['stack']);
228 8
                    array_pop($context['stack']);
229 8
                    array_shift($context['partialblock']);
230 8
                    return true;
231
                }
232
            }
233 6
            $context['partialblock'][0] .= Token::toString($context['currentToken']);
234 6
            return true;
235
        }
236 650
    }
237
238
    /**
239
     * validate block begin token
240
     *
241
     * @param array<string,array|string|integer> $context current compile context
242
     * @param array<boolean|integer|string|array> $vars parsed arguments list
243
     *
244
     * @return boolean Return true always
245
     */
246 247
    protected static function blockBegin(&$context, $vars) {
247 247
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
248 5
            case 'with':
249 26
                return static::with($context, $vars);
250 5
            case 'each':
251 53
                return static::section($context, $vars, true);
252 5
            case 'unless':
253 6
                return static::unless($context, $vars);
254 115
            case 'if':
255 63
                return static::doIf($context, $vars);
256
            default:
257 115
                return static::section($context, $vars);
258
        }
259
    }
260
261
    /**
262
     * validate builtin helpers
263
     *
264
     * @param array<string,array|string|integer> $context current compile context
265
     * @param array<boolean|integer|string|array> $vars parsed arguments list
266
     */
267 134
    protected static function builtin(&$context, $vars) {
268 134
        if ($context['flags']['nohbh']) {
269 8
            if (isset($vars[1][0])) {
270 8
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
271
            }
272
        } else {
273 126
            if (count($vars) < 2) {
274 5
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
275
            }
276
        }
277 134
        $context['usedFeature'][$vars[0][0]]++;
278 134
    }
279
280
    /**
281
     * validate section token
282
     *
283
     * @param array<string,array|string|integer> $context current compile context
284
     * @param array<boolean|integer|string|array> $vars parsed arguments list
285
     * @param boolean $isEach the section is #each
286
     *
287
     * @return boolean Return true always
288
     */
289 168
    protected static function section(&$context, $vars, $isEach = false) {
290 168
        if ($isEach) {
291 53
            static::builtin($context, $vars);
292
        } else {
293 115
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
294 1
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
295
            }
296 115
            $context['usedFeature']['sec']++;
297
        }
298 168
        return true;
299
    }
300
301
    /**
302
     * validate with token
303
     *
304
     * @param array<string,array|string|integer> $context current compile context
305
     * @param array<boolean|integer|string|array> $vars parsed arguments list
306
     *
307
     * @return boolean Return true always
308
     */
309 26
    protected static function with(&$context, $vars) {
310 26
        static::builtin($context, $vars);
311 26
        return true;
312
    }
313
314
    /**
315
     * validate unless token
316
     *
317
     * @param array<string,array|string|integer> $context current compile context
318
     * @param array<boolean|integer|string|array> $vars parsed arguments list
319
     *
320
     * @return boolean Return true always
321
     */
322 6
    protected static function unless(&$context, $vars) {
323 6
        static::builtin($context, $vars);
324 6
        return true;
325
    }
326
327
    /**
328
     * validate if token
329
     *
330
     * @param array<string,array|string|integer> $context current compile context
331
     * @param array<boolean|integer|string|array> $vars parsed arguments list
332
     *
333
     * @return boolean Return true always
334
     */
335 63
    protected static function doIf(&$context, $vars) {
336 63
        static::builtin($context, $vars);
337 63
        return true;
338
    }
339
340
    /**
341
     * validate block custom helper token
342
     *
343
     * @param array<string,array|string|integer> $context current compile context
344
     * @param array<boolean|integer|string|array> $vars parsed arguments list
345
     * @param boolean $inverted the logic will be inverted
346
     *
347
     * @return string|null Return compiled code segment for the token
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|null?

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

Loading history...
348
     */
349 61
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
350 61
        if (is_string($vars[0][0])) {
351
            // detect handlebars custom helpers.
352 61
            if (isset($context['hbhelpers'][$vars[0][0]])) {
353 58
                return ++$context['usedFeature']['hbhelper'];
354
            }
355
356
            // detect block custom helpers.
357 3
            if (isset($context['blockhelpers'][$vars[0][0]])) {
358 3
                return ++$context['usedFeature']['bhelper'];
359
            }
360
        }
361
    }
362
363
    /**
364
     * validate inverted section
365
     *
366
     * @param array<string,array|string|integer> $context current compile context
367
     * @param array<boolean|integer|string|array> $vars parsed arguments list
368
     *
369
     * @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...
370
     */
371 38
    protected static function invertedSection(&$context, $vars) {
372 38
        return ++$context['usedFeature']['isec'];
373
    }
374
375
    /**
376
     * Return compiled PHP code for a handlebars block end token
377
     *
378
     * @param array<string,array|string|integer> $context current compile context
379
     * @param array<boolean|integer|string|array> $vars parsed arguments list
380
     *
381
     * @return boolean Return true
382
     */
383 332
    protected static function blockEnd(&$context, $vars) {
384 332
        $context['level']--;
385 332
        $c = count($context['stack']) - 2;
386 332
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
387 332
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
388 332
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
389 313
            case 'with':
390 32
                if (!$context['flags']['nohbh']) {
391 30
                    if ($pop2 !== '[with]') {
392 3
                        $context['error'][] = 'Unexpect token: {{/with}} !';
393 3
                        return;
394
                    }
395
                }
396 29
                return true;
397
        }
398
399
        switch($pop) {
400 313
            case '#>':
401
            case '#':
402 1
            case '^':
403 312
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
404 312
                $v = Expression::toString($levels, $spvar, $var);
405 312
                if ($pop2 !== $v) {
406 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
407 2
                    return;
408
                }
409 310
                if ($pop == '^') {
410 38
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
411
                }
412 280
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
413
            default:
414 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
415 1
                return;
416
        }
417
    }
418
419
    /**
420
     * handle delimiter change
421
     *
422
     * @param array<string,array|string|integer> $context current compile context
423
     *
424
     * @return boolean|null Return true when delimiter changed
425
     */
426 679
    protected static function isDelimiter(&$context) {
427 679
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
428 15
            $context['usedFeature']['delimiter']++;
429 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
430 15
            return true;
431
        }
432 670
    }
433
434
    /**
435
     * handle raw block
436
     *
437
     * @param string[] $token detected handlebars {{ }} token
438
     * @param array<string,array|string|integer> $context current compile context
439
     *
440
     * @return boolean|null Return true when in rawblock mode
441
     */
442 689
    protected static function rawblock(&$token, &$context) {
443 689
        $inner = $token[Token::POS_INNERTAG];
444 689
        trim($inner);
445
446
        // skip parse when inside raw block
447 689
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
448 4
            return true;
449
        }
450
451 689
        $token[Token::POS_INNERTAG] = $inner;
452
453
        // Handle raw block
454 689
        if ($token[Token::POS_BEGINRAW] === '{{') {
455 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
456 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
457
            }
458 7
            if ($context['rawblock']) {
459 4
                Parser::setDelimiter($context);
460 4
                $context['rawblock'] = false;
461
            } else {
462 7
                if ($token[Token::POS_OP]) {
463 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
464
                }
465 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
466 7
                Parser::setDelimiter($context);
467 7
                $token[Token::POS_OP] = '#';
468
            }
469 7
            $token[Token::POS_ENDRAW] = '}}';
470
        }
471 689
    }
472
473
    /**
474
     * handle comment
475
     *
476
     * @param string[] $token detected handlebars {{ }} token
477
     * @param array<string,array|string|integer> $context current compile context
478
     *
479
     * @return boolean|null Return true when is comment
480
     */
481 670
    protected static function comment(&$token, &$context) {
482 670
        if ($token[Token::POS_OP] === '!') {
483 25
            $context['usedFeature']['comment']++;
484 25
            return true;
485
        }
486 650
    }
487
488
    /**
489
     * Collect handlebars usage information, detect template error.
490
     *
491
     * @param string[] $token detected handlebars {{ }} token
492
     * @param array<string,array|string|integer> $context current compile context
493
     */
494 689
    protected static function token(&$token, &$context) {
495 689
        $context['currentToken'] = &$token;
496
497 689
        if (static::rawblock($token, $context)) {
498 4
            return Token::toString($token);
499
        }
500
501 689
        if (static::delimiter($token, $context)) {
502 10
            return;
503
        }
504
505 679
        if (static::isDelimiter($context)) {
506 15
            static::spacing($token, $context);
507 15
            return;
508
        }
509
510 670
        if (static::comment($token, $context)) {
511 25
            static::spacing($token, $context);
512 25
            return;
513
        }
514
515 650
        list($raw, $vars) = Parser::parse($token, $context);
516
517 650
        if (static::partialBlock($token[Token::POS_OP], $context, $vars)) {
518 8
            $token[Token::POS_LOTHER] = '';
519 8
            $token[Token::POS_LSPACE] = '';
520 8
            return;
521
        }
522
523
        // Handle spacing (standalone tags, partial indent)
524 650
        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));
525
526 650
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
527 384
            return array($raw, $vars);
528
        }
529
530 503
        if (count($vars) == 0) {
531 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
532
        }
533
534 497
        if (!isset($vars[0])) {
535 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
536
        }
537
538 496
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
539
540 496
        foreach ($vars as $var) {
541 496
            if (!isset($var[0]) || ($var[0] === 0)) {
542 66
                if ($context['level'] == 0) {
543 22
                    $context['usedFeature']['rootthis']++;
544
                }
545 496
                $context['usedFeature']['this']++;
546
            }
547
        }
548
549 496
        if (!isset($vars[0][0])) {
550 49
            return array($raw, $vars);
551
        }
552
553 464
        if (!static::helper($context, $vars[0][0])) {
554 355
            static::lookup($context, $vars);
555
        }
556
557 464
        return array($raw, $vars);
558
    }
559
560
    /**
561
     * Return 1 or larger number when else token detected
562
     *
563
     * @param array<string,array|string|integer> $context current compile context
564
     *
565
     * @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...
566
     */
567 23
    protected static function doElse(&$context) {
568 23
        if ($context['level'] == 0) {
569
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
570
        }
571 23
        return ++$context['usedFeature']['else'];
572
    }
573
574
    /**
575
     * Return true whe the name is listed in helper table
576
     *
577
     * @param array<string,array|string|integer> $context current compile context
578
     * @param array<boolean|integer|string|array> $vars parsed arguments list
579
     *
580
     * @return boolean Return true when it is custom helper
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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

Loading history...
581
     */
582 355
    public static function lookup(&$context, $vars) {
583 355
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
584 4
            if (!$context['flags']['nohbh']) {
585 4
                if (count($vars) < 2) {
586 1
                    $context['error'][] = "No argument after {{lookup}} !";
587 3
                } else if (count($vars) < 3) {
588 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
589
                }
590 4
                $context['usedFeature']['lookup']++;
591 4
                return true;
592
            }
593
        }
594 351
    }
595
596
    /**
597
     * Return true when the name is listed in helper table
598
     *
599
     * @param array<string,array|string|integer> $context current compile context
600
     * @param string $name token name
601
     *
602
     * @return boolean Return true when it is custom helper
603
     */
604 472
    public static function helper(&$context, $name) {
605 472
        if (isset($context['hbhelpers'][$name])) {
606 102
            $context['usedFeature']['hbhelper']++;
607 102
            return true;
608
        }
609
610 378
        if (isset($context['helpers'][$name])) {
611 23
            $context['usedFeature']['helper']++;
612 23
            return true;
613
        }
614
615 357
        return false;
616
    }
617
618
    /**
619
     * detect for block custom helper
620
     *
621
     * @param array<string,array|string|integer> $context current compile context
622
     * @param array<boolean|integer|string|array> $vars parsed arguments list
623
     *
624
     * @return boolean|null Return true when this token is block custom helper
625
     */
626 331
    protected static function isBlockHelper($context, $vars) {
627 331
        if (!isset($vars[0][0])) {
628 4
            return;
629
        }
630
631 328
        if (!isset($context['blockhelpers'][$vars[0][0]]) && !isset($context['hbhelpers'][$vars[0][0]])) {
632 273
            return;
633
        }
634
635 61
        return true;
636
    }
637
638
    /**
639
     * validate partial
640
     *
641
     * @param array<string,array|string|integer> $context current compile context
642
     * @param array<boolean|integer|string|array> $vars parsed arguments list
643
     *
644
     * @return integer|true Return 1 or larger number for runtime partial, return true for other case
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|boolean?

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

Loading history...
645
     */
646 79
    protected static function partial(&$context, $vars) {
647 79
        if (Parser::isSubExp($vars[0])) {
648 5
            if ($context['flags']['runpart']) {
649 4
                return $context['usedFeature']['dynpartial']++;
650
            } else {
651 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
652 1
                return true;
653
            }
654
        } else {
655 74
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
656 66
                Partial::readPartial($vars[0][0], $context);
657
            }
658
        }
659 74
        if (!$context['flags']['runpart']) {
660 9
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
661 9
            if ($named || (count($vars) > 1)) {
662 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
663
            }
664
        }
665
666 74
        return true;
667
    }
668
669
    /**
670
     * Modify $token when spacing rules matched.
671
     *
672
     * @param array<string> $token detected handlebars {{ }} token
673
     * @param array<string,array|string|integer> $context current compile context
674
     * @param boolean $nost do not do stand alone logic
675
     *
676
     * @return string|null Return compiled code segment for the token
677
     */
678 679
    protected static function spacing(&$token, &$context, $nost = false) {
679
        // left line change detection
680 679
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
681 679
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
682
        // right line change detection
683 679
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
684 679
        $st = true;
685
        // setup ahead flag
686 679
        $ahead = $context['tokens']['ahead'];
687 679
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
688
        // reset partial indent
689 679
        $context['tokens']['partialind'] = '';
690
        // same tags in the same line , not standalone
691 679
        if (!$lsp && $ahead) {
692 324
            $st = false;
693
        }
694 679
        if ($nost) {
695 492
            $st = false;
696
        }
697
        // not standalone because other things in the same line ahead
698 679
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
699 194
            $st = false;
700
        }
701
        // not standalone because other things in the same line behind
702 679
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
703 325
            $st = false;
704
        }
705 679
        if ($st && (($lsp && $rsp) // both side cr
706 121
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
707 679
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
708
           )) {
709
            // handle partial
710 63
            if ($token[Token::POS_OP] === '>') {
711 15
                if (!$context['flags']['noind']) {
712 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
713 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
714
                }
715
            } else {
716 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
717
            }
718 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
719
        }
720
721
        // Handle space control.
722 679
        if ($token[Token::POS_LSPACECTL]) {
723 30
            $token[Token::POS_LSPACE] = '';
724
        }
725 679
        if ($token[Token::POS_RSPACECTL]) {
726 33
            $token[Token::POS_RSPACE] = '';
727
        }
728 679
    }
729
}
730
731