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 ( 4347a7...178f3d )
by Zordius
02:13
created

Validator::spacing()   F

Complexity

Conditions 24
Paths 1920

Size

Total Lines 51
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 600

Importance

Changes 2
Bugs 2 Features 0
Metric Value
c 2
b 2
f 0
dl 0
loc 51
ccs 0
cts 0
cp 0
rs 2.9533
cc 24
eloc 30
nc 1920
nop 3
crap 600

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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::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 466
            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 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 651
    protected static function operator($operator, &$context, &$vars) {
158
        switch ($operator) {
159 651
            case '#*':
160 8
                $context['stack'][] = count($context['parsed'][0]);
161 8
                static::pushStack($context, '#*', $vars);
162 8
                array_unshift($context['inlinepartial'], '');
163
                return static::inline($context, $vars);
164
165 76
            case '#>':
166
                $context['stack'][] = count($context['parsed'][0]);
167
                static::pushStack($context, '#>', $vars);
168 66
                array_unshift($context['partialblock'], '');
169 24
                return static::partial($context, $vars);
170 1
171 1
            case '>':
172
                return static::partial($context, $vars);
173 23
174
            case '^':
175
                if (!isset($vars[0][0])) {
176
                    if (!$context['flags']['else']) {
177 42
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
178 3
                        return;
179 3
                    } else {
180
                        return static::doElse($context);
181
                    }
182 39
                }
183 39
184
                if (static::isBlockHelper($context, $vars)) {
185
                    static::pushStack($context, '#', $vars);
186 329
                    return static::blockCustomHelper($context, $vars, true);
187 329
                }
188 329
189 329
                static::pushStack($context, '^', $vars);
190 329
                return static::invertedSection($context, $vars);
191
192 504
            case '/':
193 302
                $r = static::blockEnd($context, $vars);
194
                array_pop($context['stack']);
195 302
                array_pop($context['stack']);
196 59
                array_pop($context['stack']);
197
                return $r;
198
199 250
            case '#':
200
                static::pushStack($context, '#', $vars);
201 504
202
                if (static::isBlockHelper($context, $vars)) {
203
                    return static::blockCustomHelper($context, $vars);
204
                }
205
206
                return static::blockBegin($context, $vars);
207
        }
208
    }
209
210
    /**
211
     * validate inline partial begin token
212 650
     *
213 650
     * @param array<string,array|string|integer> $context current compile context
214 8
     * @param array<boolean|integer|string|array> $vars parsed arguments list
215 8
     *
216 8
     * @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 8
     */
218 8
    protected static function inlinePartial(&$context, $vars) {
219 8
        if (count($context['inlinepartial']) > 0) {
220 8
            $context['inlinepartial'][0] .= $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
221 8
            $context['currentToken'][Token::POS_LOTHER] = '';
222 8
            $context['currentToken'][Token::POS_LSPACE] = '';
223 8
            if ($context['currentToken'][Token::POS_OP] === '/') {
224 5
                if (static::blockEnd($context, $vars) !== null) {
225
                    $context['usedFeature']['inlpartial']++;
226 8
                    $code = Partial::compileLocal($context, $context['inlinepartial'][0]);
227 8
                    array_shift($context['inlinepartial']);
228 8
                    $c = $context['stack'][count($context['stack']) - 4];
229 8
                    array_pop($context['stack']);
230 8
                    array_pop($context['stack']);
231 8
                    array_pop($context['stack']);
232
                    array_pop($context['stack']);
233
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 2);
234 5
                    $context['parsed'][0][$c][1][0][0] = $code;
235 5
                    $context['inlines'][$context['parsed'][0][$c][1][1][0]] = true;
236
                    return true;
237 650
                }
238
            }
239
            $context['inlinepartial'][0] .= Token::toString($context['currentToken']);
240
        }
241
    }
242
243
    /**
244
     * validate partial block token
245
     *
246
     * @param array<string,array|string|integer> $context current compile context
247 249
     * @param array<boolean|integer|string|array> $vars parsed arguments list
248 249
     *
249 5
     * @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...
250 28
     */
251 5
    protected static function partialBlock(&$context, $vars) {
252 53
        if (count($context['partialblock']) > 0) {
253 5
            $context['partialblock'][0] .= $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
254 6
            $context['currentToken'][Token::POS_LOTHER] = '';
255 115
            $context['currentToken'][Token::POS_LSPACE] = '';
256 63
            if ($context['currentToken'][Token::POS_OP] === '/') {
257
                if (static::blockEnd($context, $vars) !== null) {
258 115
                    $found = Partial::resolvePartial($vars[0][0], $context) !== null;
259
                    $v = $found ? '@partial-block' : $vars[0][0];
260
                    $context['usedPartial'][$v] = $context['partialblock'][0];
261
                    Partial::compileDynamic($v, $context);
262
                    if ($found) {
263
                        Partial::readPartial($vars[0][0], $context);
264
                    }
265
                    array_shift($context['partialblock']);
266
                    $c = $context['stack'][count($context['stack']) - 4];
267
                    array_pop($context['stack']);
268 136
                    array_pop($context['stack']);
269 136
                    array_pop($context['stack']);
270 8
                    array_pop($context['stack']);
271 8
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
272
                    return true;
273
                }
274 128
            }
275 5
            $context['partialblock'][0] .= Token::toString($context['currentToken']);
276
        }
277
    }
278 136
279 136
    /**
280
     * validate block begin token
281
     *
282
     * @param array<string,array|string|integer> $context current compile context
283
     * @param array<boolean|integer|string|array> $vars parsed arguments list
284
     *
285
     * @return boolean Return true always
286
     */
287
    protected static function blockBegin(&$context, $vars) {
288
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
289
            case 'with':
290 168
                return static::with($context, $vars);
291 168
            case 'each':
292 53
                return static::section($context, $vars, true);
293
            case 'unless':
294 115
                return static::unless($context, $vars);
295 1
            case 'if':
296
                return static::doIf($context, $vars);
297 115
            default:
298
                return static::section($context, $vars);
299 168
        }
300
    }
301
302
    /**
303
     * validate builtin helpers
304
     *
305
     * @param array<string,array|string|integer> $context current compile context
306
     * @param array<boolean|integer|string|array> $vars parsed arguments list
307
     */
308
    protected static function builtin(&$context, $vars) {
309
        if ($context['flags']['nohbh']) {
310 28
            if (isset($vars[1][0])) {
311 28
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
312 28
            }
313
        } else {
314
            if (count($vars) < 2) {
315
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
316
            }
317
        }
318
        $context['usedFeature'][$vars[0][0]]++;
319
    }
320
321
    /**
322
     * validate section token
323 6
     *
324 6
     * @param array<string,array|string|integer> $context current compile context
325 6
     * @param array<boolean|integer|string|array> $vars parsed arguments list
326
     * @param boolean $isEach the section is #each
327
     *
328
     * @return boolean Return true always
329
     */
330
    protected static function section(&$context, $vars, $isEach = false) {
331
        if ($isEach) {
332
            static::builtin($context, $vars);
333
        } else {
334
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
335
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
336 63
            }
337 63
            $context['usedFeature']['sec']++;
338 63
        }
339
        return true;
340
    }
341
342
    /**
343
     * validate with token
344
     *
345
     * @param array<string,array|string|integer> $context current compile context
346
     * @param array<boolean|integer|string|array> $vars parsed arguments list
347
     *
348
     * @return boolean Return true always
349
     */
350 61
    protected static function with(&$context, $vars) {
351 61
        static::builtin($context, $vars);
352
        return true;
353 61
    }
354 58
355
    /**
356
     * validate unless token
357
     *
358 3
     * @param array<string,array|string|integer> $context current compile context
359 3
     * @param array<boolean|integer|string|array> $vars parsed arguments list
360
     *
361
     * @return boolean Return true always
362
     */
363
    protected static function unless(&$context, $vars) {
364
        static::builtin($context, $vars);
365
        return true;
366
    }
367
368
    /**
369
     * validate if token
370
     *
371
     * @param array<string,array|string|integer> $context current compile context
372 38
     * @param array<boolean|integer|string|array> $vars parsed arguments list
373 38
     *
374
     * @return boolean Return true always
375
     */
376
    protected static function doIf(&$context, $vars) {
377
        static::builtin($context, $vars);
378
        return true;
379
    }
380
381
    /**
382
     * validate block custom helper token
383
     *
384 332
     * @param array<string,array|string|integer> $context current compile context
385 332
     * @param array<boolean|integer|string|array> $vars parsed arguments list
386 332
     * @param boolean $inverted the logic will be inverted
387 332
     *
388 332
     * @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...
389 332
     */
390 313
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
391 32
        if (is_string($vars[0][0])) {
392 30
            // detect handlebars custom helpers.
393 1
            if (isset($context['hbhelpers'][$vars[0][0]])) {
394 1
                return ++$context['usedFeature']['hbhelper'];
395
            }
396
397 31
            // detect block custom helpers.
398
            if (isset($context['blockhelpers'][$vars[0][0]])) {
399
                return ++$context['usedFeature']['bhelper'];
400
            }
401 313
        }
402
    }
403 1
404 312
    /**
405 312
     * validate inverted section
406 312
     *
407 2
     * @param array<string,array|string|integer> $context current compile context
408 2
     * @param array<boolean|integer|string|array> $vars parsed arguments list
409
     *
410 310
     * @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...
411 38
     */
412
    protected static function invertedSection(&$context, $vars) {
413 280
        return ++$context['usedFeature']['isec'];
414
    }
415 1
416 1
    /**
417
     * Return compiled PHP code for a handlebars block end token
418
     *
419
     * @param array<string,array|string|integer> $context current compile context
420
     * @param array<boolean|integer|string|array> $vars parsed arguments list
421
     *
422
     * @return boolean Return true
423
     */
424
    protected static function blockEnd(&$context, $vars) {
425
        $context['level']--;
426
        $c = count($context['stack']) - 2;
427 679
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
428 679
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
429 15
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
430 15
            case 'with':
431 15
                if (!$context['flags']['nohbh']) {
432
                    if ($pop2 !== '[with]') {
433 670
                        $context['error'][] = 'Unexpect token: {{/with}} !';
434
                        return;
435
                    }
436
                }
437
                return true;
438
        }
439
440
        switch($pop) {
441
            case '#>':
442
            case '#*':
443 689
            case '#':
444 689
            case '^':
445 689
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
446
                $v = Expression::toString($levels, $spvar, $var);
447
                if ($pop2 !== $v) {
448 689
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
449 4
                    return;
450
                }
451
                if ($pop == '^') {
452 689
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
453
                }
454
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
455 689
            default:
456 7
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
457 1
                return;
458
        }
459 7
    }
460 4
461 4
    /**
462
     * handle delimiter change
463 7
     *
464 1
     * @param array<string,array|string|integer> $context current compile context
465
     *
466 7
     * @return boolean|null Return true when delimiter changed
467 7
     */
468 7
    protected static function isDelimiter(&$context) {
469
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
470 7
            $context['usedFeature']['delimiter']++;
471
            Parser::setDelimiter($context, $matched[1], $matched[2]);
472 689
            return true;
473
        }
474
    }
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 670
     * @return boolean|null Return true when in rawblock mode
483 670
     */
484 25
    protected static function rawblock(&$token, &$context) {
485 25
        $inner = $token[Token::POS_INNERTAG];
486
        trim($inner);
487 650
488
        // skip parse when inside raw block
489
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
490
            return true;
491
        }
492
493
        $token[Token::POS_INNERTAG] = $inner;
494
495 689
        // Handle raw block
496 689
        if ($token[Token::POS_BEGINRAW] === '{{') {
497
            if ($token[Token::POS_ENDRAW] !== '}}') {
498 689
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
499 4
            }
500
            if ($context['rawblock']) {
501
                Parser::setDelimiter($context);
502 689
                $context['rawblock'] = false;
503 10
            } else {
504
                if ($token[Token::POS_OP]) {
505
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
506 679
                }
507 15
                $context['rawblock'] = $token[Token::POS_INNERTAG];
508 15
                Parser::setDelimiter($context);
509
                $token[Token::POS_OP] = '#';
510
            }
511 670
            $token[Token::POS_ENDRAW] = '}}';
512 25
        }
513 25
    }
514
515
    /**
516 650
     * handle comment
517
     *
518 650
     * @param string[] $token detected handlebars {{ }} token
519 8
     * @param array<string,array|string|integer> $context current compile context
520 8
     *
521 8
     * @return boolean|null Return true when is comment
522
     */
523
    protected static function comment(&$token, &$context) {
524
        if ($token[Token::POS_OP] === '!') {
525 650
            $context['usedFeature']['comment']++;
526
            return true;
527 650
        }
528 384
    }
529
530
    /**
531 506
     * Collect handlebars usage information, detect template error.
532 6
     *
533
     * @param string[] $token detected handlebars {{ }} token
534
     * @param array<string,array|string|integer> $context current compile context
535 500
     */
536 1
    protected static function token(&$token, &$context) {
537
        $context['currentToken'] = &$token;
538
539 499
        if (static::rawblock($token, $context)) {
540
            return Token::toString($token);
541 499
        }
542 499
543 66
        if (static::delimiter($token, $context)) {
544 22
            return;
545
        }
546 499
547
        if (static::isDelimiter($context)) {
548
            static::spacing($token, $context);
549
            return;
550 499
        }
551 49
552
        if (static::comment($token, $context)) {
553
            static::spacing($token, $context);
554 467
            return;
555 358
        }
556
557
        list($raw, $vars) = Parser::parse($token, $context);
558 467
559
        if (static::partialBlock($context, $vars)) {
560
            return;
561
        }
562
        if (static::inlinePartial($context, $vars)) {
563
            return;
564
        }
565
566
        // Handle spacing (standalone tags, partial indent)
567
        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));
568 23
569 23
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
570
            return array($raw, $vars);
571
        }
572 23
573
        if (count($vars) == 0) {
574
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
575
        }
576
577
        if (!isset($vars[0])) {
578
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
579
        }
580
581
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
582
583 358
        foreach ($vars as $var) {
584 358
            if (!isset($var[0]) || ($var[0] === 0)) {
585 4
                if ($context['level'] == 0) {
586 4
                    $context['usedFeature']['rootthis']++;
587 1
                }
588 3
                $context['usedFeature']['this']++;
589 1
            }
590
        }
591 4
592 4
        if (!isset($vars[0][0])) {
593
            return array($raw, $vars);
594
        }
595 354
596
        if (!static::helper($context, $vars[0][0])) {
597
            static::lookup($context, $vars);
598
        }
599
600
        return array($raw, $vars);
601
    }
602
603
    /**
604
     * Return 1 or larger number when else token detected
605 475
     *
606 475
     * @param array<string,array|string|integer> $context current compile context
607 102
     *
608 102
     * @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...
609
     */
610
    protected static function doElse(&$context) {
611 381
        if ($context['level'] == 0) {
612 23
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
613 23
        }
614
        return ++$context['usedFeature']['else'];
615
    }
616 360
617
    /**
618
     * Return true whe the name is listed in helper table
619
     *
620
     * @param array<string,array|string|integer> $context current compile context
621
     * @param array<boolean|integer|string|array> $vars parsed arguments list
622
     *
623
     * @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...
624
     */
625
    public static function lookup(&$context, $vars) {
626
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
627 333
            if (!$context['flags']['nohbh']) {
628 333
                if (count($vars) < 2) {
629 4
                    $context['error'][] = "No argument after {{lookup}} !";
630
                } else if (count($vars) < 3) {
631
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
632 330
                }
633 275
                $context['usedFeature']['lookup']++;
634
                return true;
635
            }
636 61
        }
637
    }
638
639
    /**
640
     * Return true when the name is listed in helper table
641
     *
642
     * @param array<string,array|string|integer> $context current compile context
643
     * @param string $name token name
644
     *
645
     * @return boolean Return true when it is custom helper
646
     */
647 79
    public static function helper(&$context, $name) {
648 79
        if (isset($context['hbhelpers'][$name])) {
649 5
            $context['usedFeature']['hbhelper']++;
650 4
            return true;
651
        }
652 1
653 1
        if (isset($context['helpers'][$name])) {
654
            $context['usedFeature']['helper']++;
655
            return true;
656 74
        }
657 70
658
        return false;
659
    }
660 74
661 9
    /**
662 9
     * detect for block custom helper
663 1
     *
664
     * @param array<string,array|string|integer> $context current compile context
665
     * @param array<boolean|integer|string|array> $vars parsed arguments list
666
     *
667 74
     * @return boolean|null Return true when this token is block custom helper
668
     */
669
    protected static function isBlockHelper($context, $vars) {
670
        if (!isset($vars[0][0])) {
671
            return;
672
        }
673
674
        if (!isset($context['blockhelpers'][$vars[0][0]]) && !isset($context['hbhelpers'][$vars[0][0]])) {
675
            return;
676
        }
677
678
        return true;
679 679
    }
680
681 679
    /**
682 679
     * validate inline partial
683
     *
684 679
     * @param array<string,array|string|integer> $context current compile context
685 679
     * @param array<boolean|integer|string|array> $vars parsed arguments list
686
     *
687 679
     * @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...
688 679
     */
689
    protected static function inline(&$context, $vars) {
690 679
        if (!$context['flags']['runpart']) {
691
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
692 679
        }
693 327
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
694
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
695 679
        }
696 495
        if (!isset($vars[1][0])) {
697
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
698
        }
699 679
    }
700 194
701
    /**
702
     * validate partial
703 679
     *
704 325
     * @param array<string,array|string|integer> $context current compile context
705
     * @param array<boolean|integer|string|array> $vars parsed arguments list
706 679
     *
707 121
     * @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...
708 679
     */
709
    protected static function partial(&$context, $vars) {
710
        if (Parser::isSubExp($vars[0])) {
711 63
            if ($context['flags']['runpart']) {
712 15
                return $context['usedFeature']['dynpartial']++;
713 10
            } else {
714 15
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
715
                return true;
716
            }
717 52
        } else {
718
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
719 63
                Partial::readPartial($vars[0][0], $context);
720
            }
721
        }
722
        if (!$context['flags']['runpart']) {
723 679
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
724 30
            if ($named || (count($vars) > 1)) {
725
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
726 679
            }
727 33
        }
728
729 679
        return true;
730
    }
731
732
    /**
733
     * Modify $token when spacing rules matched.
734
     *
735
     * @param array<string> $token detected handlebars {{ }} token
736
     * @param array<string,array|string|integer> $context current compile context
737
     * @param boolean $nost do not do stand alone logic
738
     *
739
     * @return string|null Return compiled code segment for the token
740
     */
741
    protected static function spacing(&$token, &$context, $nost = false) {
742
        // left line change detection
743
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
744
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
745
        // right line change detection
746
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
747
        $st = true;
748
        // setup ahead flag
749
        $ahead = $context['tokens']['ahead'];
750
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
751
        // reset partial indent
752
        $context['tokens']['partialind'] = '';
753
        // same tags in the same line , not standalone
754
        if (!$lsp && $ahead) {
755
            $st = false;
756
        }
757
        if ($nost) {
758
            $st = false;
759
        }
760
        // not standalone because other things in the same line ahead
761
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
762
            $st = false;
763
        }
764
        // not standalone because other things in the same line behind
765
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
766
            $st = false;
767
        }
768
        if ($st && (($lsp && $rsp) // both side cr
769
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
770
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
771
           )) {
772
            // handle partial
773
            if ($token[Token::POS_OP] === '>') {
774
                if (!$context['flags']['noind']) {
775
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
776
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
777
                }
778
            } else {
779
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
780
            }
781
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
782
        }
783
784
        // Handle space control.
785
        if ($token[Token::POS_LSPACECTL]) {
786
            $token[Token::POS_LSPACE] = '';
787
        }
788
        if ($token[Token::POS_RSPACECTL]) {
789
            $token[Token::POS_RSPACE] = '';
790
        }
791
    }
792
}
793
794