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 ( 178f3d...f4df6d )
by Zordius
02:49
created

Validator::section()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.2
cc 4
eloc 8
nc 3
nop 3
crap 4
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 712
    public static function verify(&$context, $template) {
39 712
        $template = SafeString::stripExtendedComments($template);
40 712
        $context['level'] = 0;
41 712
        Parser::setDelimiter($context);
42
43 712
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 701
            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 699
            $context['tokens']['count']++;
56 699
            $V = static::token($matches, $context);
57 699
            static::pushToken($context, $matches[Token::POS_LOTHER]);
58 699
            static::pushToken($context, $matches[Token::POS_LSPACE]);
59 699
            if ($V) {
60 660
                if (is_array($V)) {
61 653
                    array_push($V, $matches, $context['tokens']['partialind']);
62
                }
63 660
                static::pushToken($context, $V);
64
            }
65 699
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
66
        }
67 712
        static::pushToken($context, $template);
68
69 712
        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 712
    }
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 712
    protected static function pushToken(&$context, $token) {
84 712
        if ($token === '') {
85 657
            return;
86
        }
87 701
        if (is_string($token)) {
88 475
            if (is_string(end($context['parsed'][0]))) {
89 223
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
90 223
                return;
91
            }
92
        }
93 701
        $context['parsed'][0][] = $token;
94 701
    }
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 346
    protected static function pushStack(&$context, $operation, $vars) {
104 346
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
105 346
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
106 346
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
107 346
        $context['stack'][] = $operation;
108 346
        $context['level']++;
109 346
    }
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 700
    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 700
        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 694
        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 690
    }
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 boolean|null|integer|double?

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

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

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

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

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

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

Loading history...
418
     *
419
     * @return boolean Return true
420
     */
421 341
    protected static function blockEnd(&$context, $vars, $match = NULL) {
422 341
        $context['level']--;
423 341
        $c = count($context['stack']) - 2;
424 341
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
425 341
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
426 341
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
427 322
            case 'with':
428 35
                if (!$context['flags']['nohbh']) {
429 33
                    if ($pop2 !== '[with]') {
430 1
                        $context['error'][] = 'Unexpect token: {{/with}} !';
431 1
                        return;
432
                    }
433
                }
434 34
                return true;
435
        }
436
437
        switch($pop) {
438 322
            case '#>':
439
            case '#*':
440
            case '#':
441 1
            case '^':
442 321
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
443 321
                $v = Expression::toString($levels, $spvar, $var);
444 321
                if ($pop2 !== $v) {
445 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
446 2
                    return;
447
                }
448 319
                if (($match !== null) && ($match !== $pop)) {
449 1
                    return;
450
                }
451 319
                if ($pop == '^') {
452 38
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
453
                }
454 289
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
455
            default:
456 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
457 1
                return;
458
        }
459
    }
460
461
    /**
462
     * handle delimiter change
463
     *
464
     * @param array<string,array|string|integer> $context current compile context
465
     *
466
     * @return boolean|null Return true when delimiter changed
467
     */
468 689
    protected static function isDelimiter(&$context) {
469 689
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
470 15
            $context['usedFeature']['delimiter']++;
471 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
472 15
            return true;
473
        }
474 680
    }
475
476
    /**
477
     * handle raw block
478
     *
479
     * @param string[] $token detected handlebars {{ }} token
480
     * @param array<string,array|string|integer> $context current compile context
481
     *
482
     * @return boolean|null Return true when in rawblock mode
483
     */
484 699
    protected static function rawblock(&$token, &$context) {
485 699
        $inner = $token[Token::POS_INNERTAG];
486 699
        trim($inner);
487
488
        // skip parse when inside raw block
489 699
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
490 4
            return true;
491
        }
492
493 699
        $token[Token::POS_INNERTAG] = $inner;
494
495
        // Handle raw block
496 699
        if ($token[Token::POS_BEGINRAW] === '{{') {
497 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
498 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
499
            }
500 7
            if ($context['rawblock']) {
501 4
                Parser::setDelimiter($context);
502 4
                $context['rawblock'] = false;
503
            } else {
504 7
                if ($token[Token::POS_OP]) {
505 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
506
                }
507 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
508 7
                Parser::setDelimiter($context);
509 7
                $token[Token::POS_OP] = '#';
510
            }
511 7
            $token[Token::POS_ENDRAW] = '}}';
512
        }
513 699
    }
514
515
    /**
516
     * handle comment
517
     *
518
     * @param string[] $token detected handlebars {{ }} token
519
     * @param array<string,array|string|integer> $context current compile context
520
     *
521
     * @return boolean|null Return true when is comment
522
     */
523 680
    protected static function comment(&$token, &$context) {
524 680
        if ($token[Token::POS_OP] === '!') {
525 25
            $context['usedFeature']['comment']++;
526 25
            return true;
527
        }
528 660
    }
529
530
    /**
531
     * Collect handlebars usage information, detect template error.
532
     *
533
     * @param string[] $token detected handlebars {{ }} token
534
     * @param array<string,array|string|integer> $context current compile context
535
     */
536 699
    protected static function token(&$token, &$context) {
537 699
        $context['currentToken'] = &$token;
538
539 699
        if (static::rawblock($token, $context)) {
540 4
            return Token::toString($token);
541
        }
542
543 699
        if (static::delimiter($token, $context)) {
544 10
            return;
545
        }
546
547 689
        if (static::isDelimiter($context)) {
548 15
            static::spacing($token, $context);
549 15
            return;
550
        }
551
552 680
        if (static::comment($token, $context)) {
553 25
            static::spacing($token, $context);
554 25
            return;
555
        }
556
557 660
        list($raw, $vars) = Parser::parse($token, $context);
558
559 660
        $partials = static::partialBlock($context, $vars);
560 660
        $partials = static::inlinePartial($context, $vars) || $partials;
561
562 660
        if ($partials) {
563 17
            $context['currentToken'][Token::POS_LOTHER] = '';
564 17
            $context['currentToken'][Token::POS_LSPACE] = '';
565 17
            return;
566
        }
567
568
        // Handle spacing (standalone tags, partial indent)
569 660
        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));
570
571 660
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
572 394
            return array($raw, $vars);
573
        }
574
575 514
        if (count($vars) == 0) {
576 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
577
        }
578
579 508
        if (!isset($vars[0])) {
580 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
581
        }
582
583 507
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
584
585 507
        foreach ($vars as $var) {
586 507
            if (!isset($var[0]) || ($var[0] === 0)) {
587 66
                if ($context['level'] == 0) {
588 22
                    $context['usedFeature']['rootthis']++;
589
                }
590 507
                $context['usedFeature']['this']++;
591
            }
592
        }
593
594 507
        if (!isset($vars[0][0])) {
595 49
            return array($raw, $vars);
596
        }
597
598 475
        if (!static::helper($context, $vars[0][0])) {
599 366
            static::lookup($context, $vars);
600
        }
601
602 475
        return array($raw, $vars);
603
    }
604
605
    /**
606
     * Return 1 or larger number when else token detected
607
     *
608
     * @param array<string,array|string|integer> $context current compile context
609
     *
610
     * @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...
611
     */
612 23
    protected static function doElse(&$context) {
613 23
        if ($context['level'] == 0) {
614
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
615
        }
616 23
        return ++$context['usedFeature']['else'];
617
    }
618
619
    /**
620
     * Return true whe the name is listed in helper table
621
     *
622
     * @param array<string,array|string|integer> $context current compile context
623
     * @param array<boolean|integer|string|array> $vars parsed arguments list
624
     *
625
     * @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...
626
     */
627 366
    public static function lookup(&$context, $vars) {
628 366
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
629 4
            if (!$context['flags']['nohbh']) {
630 4
                if (count($vars) < 2) {
631 1
                    $context['error'][] = "No argument after {{lookup}} !";
632 3
                } else if (count($vars) < 3) {
633 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
634
                }
635 4
                $context['usedFeature']['lookup']++;
636 4
                return true;
637
            }
638
        }
639 362
    }
640
641
    /**
642
     * Return true when the name is listed in helper table
643
     *
644
     * @param array<string,array|string|integer> $context current compile context
645
     * @param string $name token name
646
     *
647
     * @return boolean Return true when it is custom helper
648
     */
649 483
    public static function helper(&$context, $name) {
650 483
        if (isset($context['hbhelpers'][$name])) {
651 102
            $context['usedFeature']['hbhelper']++;
652 102
            return true;
653
        }
654
655 389
        if (isset($context['helpers'][$name])) {
656 23
            $context['usedFeature']['helper']++;
657 23
            return true;
658
        }
659
660 368
        return false;
661
    }
662
663
    /**
664
     * detect for block custom helper
665
     *
666
     * @param array<string,array|string|integer> $context current compile context
667
     * @param array<boolean|integer|string|array> $vars parsed arguments list
668
     *
669
     * @return boolean|null Return true when this token is block custom helper
670
     */
671 336
    protected static function isBlockHelper($context, $vars) {
672 336
        if (!isset($vars[0][0])) {
673 4
            return;
674
        }
675
676 333
        if (!isset($context['blockhelpers'][$vars[0][0]]) && !isset($context['hbhelpers'][$vars[0][0]])) {
677 278
            return;
678
        }
679
680 61
        return true;
681
    }
682
683
    /**
684
     * validate inline partial
685
     *
686
     * @param array<string,array|string|integer> $context current compile context
687
     * @param array<boolean|integer|string|array> $vars parsed arguments list
688
     *
689
     * @return boolean 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...
690
     */
691 8
    protected static function inline(&$context, $vars) {
692 8
        if (!$context['flags']['runpart']) {
693
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
694
        }
695 8
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
696
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
697
        }
698 8
        if (!isset($vars[1][0])) {
699
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
700
        }
701 8
    }
702
703
    /**
704
     * validate partial
705
     *
706
     * @param array<string,array|string|integer> $context current compile context
707
     * @param array<boolean|integer|string|array> $vars parsed arguments list
708
     *
709
     * @return 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...
710
     */
711 89
    protected static function partial(&$context, $vars) {
712 89
        if (Parser::isSubExp($vars[0])) {
713 5
            if ($context['flags']['runpart']) {
714 4
                return $context['usedFeature']['dynpartial']++;
715
            } else {
716 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
717 1
                return true;
718
            }
719
        } else {
720 84
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
721 79
                Partial::readPartial($vars[0][0], $context);
722
            }
723
        }
724 84
        if (!$context['flags']['runpart']) {
725 9
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
726 9
            if ($named || (count($vars) > 1)) {
727 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
728
            }
729
        }
730
731 84
        return true;
732
    }
733
734
    /**
735
     * Modify $token when spacing rules matched.
736
     *
737
     * @param array<string> $token detected handlebars {{ }} token
738
     * @param array<string,array|string|integer> $context current compile context
739
     * @param boolean $nost do not do stand alone logic
740
     *
741
     * @return string|null Return compiled code segment for the token
742
     */
743 689
    protected static function spacing(&$token, &$context, $nost = false) {
744
        // left line change detection
745 689
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
746 689
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
747
        // right line change detection
748 689
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
749 689
        $st = true;
750
        // setup ahead flag
751 689
        $ahead = $context['tokens']['ahead'];
752 689
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
753
        // reset partial indent
754 689
        $context['tokens']['partialind'] = '';
755
        // same tags in the same line , not standalone
756 689
        if (!$lsp && $ahead) {
757 335
            $st = false;
758
        }
759 689
        if ($nost) {
760 495
            $st = false;
761
        }
762
        // not standalone because other things in the same line ahead
763 689
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
764 194
            $st = false;
765
        }
766
        // not standalone because other things in the same line behind
767 689
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
768 334
            $st = false;
769
        }
770 689
        if ($st && (($lsp && $rsp) // both side cr
771 123
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
772 689
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
773
           )) {
774
            // handle partial
775 63
            if ($token[Token::POS_OP] === '>') {
776 15
                if (!$context['flags']['noind']) {
777 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
778 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
779
                }
780
            } else {
781 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
782
            }
783 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
784
        }
785
786
        // Handle space control.
787 689
        if ($token[Token::POS_LSPACECTL]) {
788 30
            $token[Token::POS_LSPACE] = '';
789
        }
790 689
        if ($token[Token::POS_RSPACECTL]) {
791 33
            $token[Token::POS_RSPACE] = '';
792
        }
793 689
    }
794
}
795
796