GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 24d0ed...428a72 )
by Zordius
03:29
created

Validator   D

Complexity

Total Complexity 178

Size/Duplication

Total Lines 842
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.2%

Importance

Changes 29
Bugs 7 Features 0
Metric Value
wmc 178
c 29
b 7
f 0
lcom 1
cbo 5
dl 0
loc 842
ccs 381
cts 388
cp 0.982
rs 4.4444

30 Methods

Rating   Name   Duplication   Size   Complexity  
A pushLeft() 0 4 1
A pushToken() 0 12 4
D verify() 0 37 10
A pushStack() 0 7 1
B delimiter() 0 12 5
C operator() 0 62 14
B inlinePartial() 0 28 5
C partialBlock() 0 32 7
A doElseIf() 0 7 2
B blockBegin() 0 17 7
A builtin() 0 12 4
A section() 0 11 4
A with() 0 4 1
A unless() 0 5 1
A doIf() 0 5 1
A blockCustomHelper() 0 8 3
A invertedSection() 0 3 1
C blockEnd() 0 40 14
A isDelimiter() 0 7 2
D rawblock() 0 30 9
A comment() 0 6 2
C token() 0 75 23
B doElse() 0 14 5
B log() 0 11 5
B lookup() 0 13 6
A helper() 0 8 2
A isBlockHelper() 0 11 3
B inline() 0 12 5
C partial() 0 22 7
F spacing() 0 51 24

How to fix   Complexity   

Complex Class

Complex classes like Validator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Validator, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2016 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file to keep LightnCandy Validator
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
use \LightnCandy\Token;
23
use \LightnCandy\Parser;
24
use \LightnCandy\Partial;
25
use \LightnCandy\Expression;
26
use \LightnCandy\SafeString;
27
28
/**
29
 * LightnCandy Validator
30
 */
31
class Validator {
32
    /**
33
     * Verify template
34
     *
35
     * @param array<string,array|string|integer> $context Current context
36
     * @param string $template handlebars template
37
     */
38 727
    public static function verify(&$context, $template) {
39 727
        $template = SafeString::stripExtendedComments($template);
40 727
        $context['level'] = 0;
41 727
        Parser::setDelimiter($context);
42
43 727
        while (preg_match($context['tokens']['search'], $template, $matches)) {
44
            // Skip a token when it is slash escaped
45 716
            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 714
            $context['tokens']['count']++;
56 714
            $V = static::token($matches, $context);
57 714
            static::pushLeft($context);
58 714
            if ($V) {
59 675
                if (is_array($V)) {
60 668
                    array_push($V, $matches, $context['tokens']['partialind']);
61
                }
62 675
                static::pushToken($context, $V);
63
            }
64 714
            $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
65
        }
66 727
        static::pushToken($context, $template);
67
68 727
        if ($context['level'] > 0) {
69 6
            array_pop($context['stack']);
70 6
            array_pop($context['stack']);
71 6
            $token = array_pop($context['stack']);
72 6
            $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : "{{#{$token}}}") . ' !!';
73
        }
74 727
    }
75
76
    /**
77
     * push left string of current token and clear it
78
     *
79
     * @param array<string,array|string|integer> $context Current context
80
     */
81 714
    protected static function pushLeft(&$context) {
82 714
        static::pushToken($context, $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]);
83 714
        $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = '';
84 714
    }
85
86
    /**
87
     * push a token into the stack when it is not empty string
88
     *
89
     * @param array<string,array|string|integer> $context Current context
90
     * @param string|array $token a parsed token or a string
91
     */
92 727
    protected static function pushToken(&$context, $token) {
93 727
        if ($token === '') {
94 594
            return;
95
        }
96 716
        if (is_string($token)) {
97 481
            if (is_string(end($context['parsed'][0]))) {
98 31
                $context['parsed'][0][key($context['parsed'][0])] .= $token;
99 31
                return;
100
            }
101
        }
102 716
        $context['parsed'][0][] = $token;
103 716
    }
104
105
    /**
106
     * push current token into the section stack
107
     *
108
     * @param array<string,array|string|integer> $context Current context
109
     * @param string $operation operation string
110
     * @param array<boolean|integer|string|array> $vars parsed arguments list
111
     */
112 354
    protected static function pushStack(&$context, $operation, $vars) {
113 354
        list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
114 354
        $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
115 354
        $context['stack'][] = Expression::toString($levels, $spvar, $var);
116 354
        $context['stack'][] = $operation;
117 354
        $context['level']++;
118 354
    }
119
120
    /**
121
     * Verify delimiters and operators
122
     *
123
     * @param string[] $token detected handlebars {{ }} token
124
     * @param array<string,array|string|integer> $context current compile context
125
     *
126
     * @return boolean|null Return true when invalid
127
     *
128
     * @expect null when input array_fill(0, 11, ''), array()
129
     * @expect null when input array(0, 0, 0, 0, 0, '{{', '#', '...', '}}'), array()
130
     * @expect true when input array(0, 0, 0, 0, 0, '{', '#', '...', '}'), array()
131
     */
132 715
    protected static function delimiter($token, &$context) {
133
        // {{ }}} or {{{ }} are invalid
134 715
        if (strlen($token[Token::POS_BEGINRAW]) !== strlen($token[Token::POS_ENDRAW])) {
135 6
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' or ' . Token::toString($token, array(Token::POS_BEGINRAW => '{', Token::POS_ENDRAW => '}')) . '?';
136 6
            return true;
137
        }
138
        // {{{# }}} or {{{! }}} or {{{/ }}} or {{{^ }}} are invalid.
139 709
        if ((strlen($token[Token::POS_BEGINRAW]) == 1) && $token[Token::POS_OP] && ($token[Token::POS_OP] !== '&')) {
140 5
            $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' ?';
141 5
            return true;
142
        }
143 705
    }
144
145
    /**
146
     * Verify operators
147
     *
148
     * @param string $operator the operator string
149
     * @param array<string,array|string|integer> $context current compile context
150
     * @param array<boolean|integer|string|array> $vars parsed arguments list
151
     *
152
     * @return boolean|integer|null Return true when invalid or detected
1 ignored issue
show
Documentation introduced by
Should the return type not be boolean|integer|double|null?

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

Loading history...
153
     *
154
     * @expect null when input '', array(), array()
155
     * @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0)), array(array('foo'))
156
     * @expect true when input '/', array('stack' => array('[with]', '#'), 'level' => 1, 'currentToken' => array(0,0,0,0,0,0,0,'with'), 'flags' => array('nohbh' => 0)), array(array())
157
     * @expect 4 when input '#', array('usedFeature' => array('sec' => 3), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elseif' => false, 'elselvl' => array()), array(array('x'))
158
     * @expect 5 when input '#', array('usedFeature' => array('if' => 4), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elseif' => false, 'elselvl' => array()), array(array('if'))
159
     * @expect 6 when input '#', array('usedFeature' => array('with' => 5), 'level' => 0, 'flags' => array('nohbh' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elseif' => false, 'elselvl' => array()), array(array('with'))
160
     * @expect 7 when input '#', array('usedFeature' => array('each' => 6), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elseif' => false, 'elselvl' => array()), array(array('each'))
161
     * @expect 8 when input '#', array('usedFeature' => array('unless' => 7), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elseif' => false, 'elselvl' => array()), array(array('unless'))
162
     * @expect 9 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 8), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elseif' => false, 'elselvl' => array()), array(array('abc'))
163
     * @expect 11 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 10), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elseif' => false, 'elselvl' => array()), array(array('abc'))
164
     * @expect true when input '>', array('partialresolver' => false, 'usedFeature' => array('partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elseif' => false, 'elselvl' => array()), array('test')
165
     */
166 676
    protected static function operator($operator, &$context, &$vars) {
167
        switch ($operator) {
168 676
            case '#*':
169 13
                if (!$context['compile']) {
170 13
                    static::pushLeft($context);
171 13
                    $context['stack'][] = count($context['parsed'][0]);
172 13
                    static::pushStack($context, '#*', $vars);
173 13
                    array_unshift($context['inlinepartial'], '');
174
                }
175 13
                return static::inline($context, $vars);
176
177
            case '#>':
178 14
                if (!$context['compile']) {
179 14
                    static::pushLeft($context);
180 14
                    $context['stack'][] = count($context['parsed'][0]);
181 14
                    $vars[Parser::PARTIALBLOCK] = ++$context['usedFeature']['pblock'];
182 14
                    static::pushStack($context, '#>', $vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer|string,boo...er|string|array|double>, but the function expects a array<integer,boolean|integer|string|array>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
183 14
                    array_unshift($context['partialblock'], '');
184
                }
185 14
                return static::partial($context, $vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer|string,boo...er|string|array|double>, but the function expects a array<integer,boolean|integer|string|array>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
186
187
            case '>':
188 91
                return static::partial($context, $vars);
189
190
            case '^':
191 64
                if (!isset($vars[0][0])) {
192 24
                    if (!$context['flags']['else']) {
193 1
                        $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
194 1
                        return;
195
                    } else {
196 23
                        return static::doElse($context, $vars);
197
                    }
198
                }
199
200 40
                if (static::isBlockHelper($context, $vars)) {
201 1
                    static::pushStack($context, '#', $vars);
202 1
                    return static::blockCustomHelper($context, $vars, true);
203
                }
204
205 39
                static::pushStack($context, '^', $vars);
206 39
                return static::invertedSection($context, $vars);
207
208
            case '/':
209 333
                $r = static::blockEnd($context, $vars);
210 333
                array_pop($context['stack']);
211 333
                array_pop($context['stack']);
212 333
                array_pop($context['stack']);
213 333
                return $r;
214
215 512
            case '#':
216 308
                if (static::isBlockHelper($context, $vars)) {
217 58
                    static::pushStack($context, '#', $vars);
218 58
                    return static::blockCustomHelper($context, $vars);
219
                }
220
221 257
                if (!$context['elseif']) {
222 257
                    static::pushStack($context, '#', $vars);
223
                }
224
225 257
                return static::blockBegin($context, $vars);
226
        }
227 512
    }
228
229
    /**
230
     * validate inline partial begin token
231
     *
232
     * @param array<string,array|string|integer> $context current compile context
233
     * @param array<boolean|integer|string|array> $vars parsed arguments list
234
     *
235
     * @return boolean|null Return true when inline partial ends
236
     */
237 675
    protected static function inlinePartial(&$context, $vars) {
238 675
        if (count($context['inlinepartial']) > 0) {
239 13
            $ended = false;
240 13
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
241
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
242 13
                $pb .= $append;
243 13
            });
244 13
            if ($context['currentToken'][Token::POS_OP] === '/') {
245 13
                if (static::blockEnd($context, $vars, '#*') !== null) {
246 13
                    $context['usedFeature']['inlpartial']++;
247 13
                    $tmpl = array_shift($context['inlinepartial']);
248 13
                    $c = $context['stack'][count($context['stack']) - 4];
249 13
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
250 13
                    $P = &$context['parsed'][0][$c];
251 13
                    if (isset($P[1][1][0])) {
252 12
                        $context['usedPartial'][$P[1][1][0]] = $tmpl;
253 12
                        $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]);
254
                    }
255 13
                    $ended = true;
256
                }
257
            }
258 13
            $append = Token::toString($context['currentToken']);
259
            array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) {
260 2
                $pb .= $append;
261 13
            });
262 13
            return $ended;
263
        }
264 675
    }
265
266
    /**
267
     * validate partial block token
268
     *
269
     * @param array<string,array|string|integer> $context current compile context
270
     * @param array<boolean|integer|string|array> $vars parsed arguments list
271
     *
272
     * @return boolean|null Return true when partial block ends
273
     */
274 675
    protected static function partialBlock(&$context, $vars) {
275 675
        if (count($context['partialblock']) > 0) {
276 14
            $ended = false;
277 14
            $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
278
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
279 14
                $pb .= $append;
280 14
            });
281 14
            if ($context['currentToken'][Token::POS_OP] === '/') {
282 14
                if (static::blockEnd($context, $vars, '#>') !== null) {
283 14
                    $c = $context['stack'][count($context['stack']) - 4];
284 14
                    $found = Partial::resolve($context, $vars[0][0]) !== null;
285 14
                    $v = $found ? "@partial-block{$context['usedFeature']['pblock']}" : "{$vars[0][0]}";
286 14
                    if ($found) {
287 10
                        $context['partials'][$v] = $context['partialblock'][0];
288
                    }
289 14
                    $context['usedPartial'][$v] = $context['partialblock'][0];
290 14
                    Partial::compileDynamic($context, $v);
291 14
                    if ($found) {
292 10
                        Partial::read($context, $vars[0][0]);
293
                    }
294 14
                    array_shift($context['partialblock']);
295 14
                    $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
296 14
                    $ended = true;
297
                }
298
            }
299 14
            $append = Token::toString($context['currentToken']);
300 14
            array_walk($context['partialblock'], function (&$pb) use ($context, $append) {
301 8
                $pb .= $append;
302 14
            });
303 14
            return $ended;
304
        }
305 675
    }
306
307
    /**
308
     * handle else if
309
     *
310
     * @param array<string,array|string|integer> $context current compile context
311
     */
312 76
    protected static function doElseIf(&$context) {
313 76
        if ($context['elseif']) {
314 8
            $context['elseif'] = false;
315
        } else {
316 70
            array_unshift($context['elselvl'], 0);
317
        }
318 76
    }
319
320
    /**
321
     * validate block begin token
322
     *
323
     * @param array<string,array|string|integer> $context current compile context
324
     * @param array<boolean|integer|string|array> $vars parsed arguments list
325
     *
326
     * @return boolean Return true always
327
     */
328 256
    protected static function blockBegin(&$context, $vars) {
329 256
        switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
330 5
            case 'with':
331 30
                array_unshift($context['elselvl'], 0);
332 30
                return static::with($context, $vars);
333 5
            case 'each':
334 53
                array_unshift($context['elselvl'], 0);
335 53
                return static::section($context, $vars, true);
336 5
            case 'unless':
337 7
                return static::unless($context, $vars);
338 118
            case 'if':
339 70
                return static::doIf($context, $vars);
340
            default:
341 118
                array_unshift($context['elselvl'], 0);
342 118
                return static::section($context, $vars);
343
        }
344
    }
345
346
    /**
347
     * validate builtin helpers
348
     *
349
     * @param array<string,array|string|integer> $context current compile context
350
     * @param array<boolean|integer|string|array> $vars parsed arguments list
351
     */
352 146
    protected static function builtin(&$context, $vars) {
353 146
        if ($context['flags']['nohbh']) {
354 8
            if (isset($vars[1][0])) {
355 8
                $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
356
            }
357
        } else {
358 138
            if (count($vars) < 2) {
359 5
                $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
360
            }
361
        }
362 146
        $context['usedFeature'][$vars[0][0]]++;
363 146
    }
364
365
    /**
366
     * validate section token
367
     *
368
     * @param array<string,array|string|integer> $context current compile context
369
     * @param array<boolean|integer|string|array> $vars parsed arguments list
370
     * @param boolean $isEach the section is #each
371
     *
372
     * @return boolean Return true always
373
     */
374 171
    protected static function section(&$context, $vars, $isEach = false) {
375 171
        if ($isEach) {
376 53
            static::builtin($context, $vars);
377
        } else {
378 118
            if ((count($vars) > 1) && !$context['flags']['lambda']) {
379 1
                $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
380
            }
381 118
            $context['usedFeature']['sec']++;
382
        }
383 171
        return true;
384
    }
385
386
    /**
387
     * validate with token
388
     *
389
     * @param array<string,array|string|integer> $context current compile context
390
     * @param array<boolean|integer|string|array> $vars parsed arguments list
391
     *
392
     * @return boolean Return true always
393
     */
394 30
    protected static function with(&$context, $vars) {
395 30
        static::builtin($context, $vars);
396 30
        return true;
397
    }
398
399
    /**
400
     * validate unless token
401
     *
402
     * @param array<string,array|string|integer> $context current compile context
403
     * @param array<boolean|integer|string|array> $vars parsed arguments list
404
     *
405
     * @return boolean Return true always
406
     */
407 7
    protected static function unless(&$context, $vars) {
408 7
        static::doElseIf($context);
409 7
        static::builtin($context, $vars);
410 7
        return true;
411
    }
412
413
    /**
414
     * validate if token
415
     *
416
     * @param array<string,array|string|integer> $context current compile context
417
     * @param array<boolean|integer|string|array> $vars parsed arguments list
418
     *
419
     * @return boolean Return true always
420
     */
421 70
    protected static function doIf(&$context, $vars) {
422 70
        static::doElseIf($context);
423 70
        static::builtin($context, $vars);
424 70
        return true;
425
    }
426
427
    /**
428
     * validate block custom helper token
429
     *
430
     * @param array<string,array|string|integer> $context current compile context
431
     * @param array<boolean|integer|string|array> $vars parsed arguments list
432
     * @param boolean $inverted the logic will be inverted
433
     *
434
     * @return integer|null Return number of used custom helpers
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|null?

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

Loading history...
435
     */
436 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
437 58
        if (is_string($vars[0][0])) {
438
            // detect handlebars custom helpers.
439 58
            if (isset($context['helpers'][$vars[0][0]])) {
440 58
                return ++$context['usedFeature']['helper'];
441
            }
442
        }
443
    }
444
445
    /**
446
     * validate inverted section
447
     *
448
     * @param array<string,array|string|integer> $context current compile context
449
     * @param array<boolean|integer|string|array> $vars parsed arguments list
450
     *
451
     * @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...
452
     */
453 38
    protected static function invertedSection(&$context, $vars) {
454 38
        return ++$context['usedFeature']['isec'];
455
    }
456
457
    /**
458
     * Return compiled PHP code for a handlebars block end token
459
     *
460
     * @param array<string,array|string|integer> $context current compile context
461
     * @param array<boolean|integer|string|array> $vars parsed arguments list
462
     * @param string|null $match should also match to this operator
463
     *
464
     * @return boolean Return true
0 ignored issues
show
Documentation introduced by
Should the return type not be null|boolean?

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

Loading history...
465
     */
466 349
    protected static function blockEnd(&$context, &$vars, $match = null) {
467 349
        $context['level']--;
468 349
        $c = count($context['stack']) - 2;
469 349
        $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
470 349
        if (($match !== null) && ($match !== $pop)) {
471 3
            return;
472
        }
473 349
        $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
474 349
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
475 330
            case 'with':
476 34
                if (!$context['flags']['nohbh']) {
477 32
                    if ($pop2 !== '[with]') {
478 1
                        $context['error'][] = 'Unexpect token: {{/with}} !';
479 1
                        return;
480
                    }
481
                }
482 33
                return true;
483
        }
484
485
        switch($pop) {
486 330
            case '#>':
487
            case '#*':
488
            case '#':
489 1
            case '^':
490 329
                list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
491 329
                $v = Expression::toString($levels, $spvar, $var);
492 329
                if ($pop2 !== $v) {
493 2
                    $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
494 2
                    return;
495
                }
496 327
                if (count($context['elselvl']) > 0) {
497 229
                    $vars[0][-1] = $context['elselvl'][0];
498 229
                    array_shift($context['elselvl']);
499
                }
500 327
                return true;
501
            default:
502 1
                $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
503 1
                return;
504
        }
505
    }
506
507
    /**
508
     * handle delimiter change
509
     *
510
     * @param array<string,array|string|integer> $context current compile context
511
     *
512
     * @return boolean|null Return true when delimiter changed
513
     */
514 704
    protected static function isDelimiter(&$context) {
515 704
        if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
516 15
            $context['usedFeature']['delimiter']++;
517 15
            Parser::setDelimiter($context, $matched[1], $matched[2]);
518 15
            return true;
519
        }
520 695
    }
521
522
    /**
523
     * handle raw block
524
     *
525
     * @param string[] $token detected handlebars {{ }} token
526
     * @param array<string,array|string|integer> $context current compile context
527
     *
528
     * @return boolean|null Return true when in rawblock mode
529
     */
530 714
    protected static function rawblock(&$token, &$context) {
531 714
        $inner = $token[Token::POS_INNERTAG];
532 714
        trim($inner);
533
534
        // skip parse when inside raw block
535 714
        if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
536 4
            return true;
537
        }
538
539 714
        $token[Token::POS_INNERTAG] = $inner;
540
541
        // Handle raw block
542 714
        if ($token[Token::POS_BEGINRAW] === '{{') {
543 7
            if ($token[Token::POS_ENDRAW] !== '}}') {
544 1
                $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
545
            }
546 7
            if ($context['rawblock']) {
547 4
                Parser::setDelimiter($context);
548 4
                $context['rawblock'] = false;
549
            } else {
550 7
                if ($token[Token::POS_OP]) {
551 1
                    $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
552
                }
553 7
                $context['rawblock'] = $token[Token::POS_INNERTAG];
554 7
                Parser::setDelimiter($context);
555 7
                $token[Token::POS_OP] = '#';
556
            }
557 7
            $token[Token::POS_ENDRAW] = '}}';
558
        }
559 714
    }
560
561
    /**
562
     * handle comment
563
     *
564
     * @param string[] $token detected handlebars {{ }} token
565
     * @param array<string,array|string|integer> $context current compile context
566
     *
567
     * @return boolean|null Return true when is comment
568
     */
569 695
    protected static function comment(&$token, &$context) {
570 695
        if ($token[Token::POS_OP] === '!') {
571 25
            $context['usedFeature']['comment']++;
572 25
            return true;
573
        }
574 675
    }
575
576
    /**
577
     * Collect handlebars usage information, detect template error.
578
     *
579
     * @param string[] $token detected handlebars {{ }} token
580
     * @param array<string,array|string|integer> $context current compile context
581
     */
582 714
    protected static function token(&$token, &$context) {
583 714
        $context['currentToken'] = &$token;
584
585 714
        if (static::rawblock($token, $context)) {
586 4
            return Token::toString($token);
587
        }
588
589 714
        if (static::delimiter($token, $context)) {
590 10
            return;
591
        }
592
593 704
        if (static::isDelimiter($context)) {
594 15
            static::spacing($token, $context);
595 15
            return;
596
        }
597
598 695
        if (static::comment($token, $context)) {
599 25
            static::spacing($token, $context);
600 25
            return;
601
        }
602
603 675
        list($raw, $vars) = Parser::parse($token, $context);
604
605 675
        $partials = static::partialBlock($context, $vars);
606 675
        $partials = static::inlinePartial($context, $vars) || $partials;
607
608 675
        if ($partials) {
609 24
            $context['stack'] = array_slice($context['stack'], 0, -4);
610 24
            $context['currentToken'][Token::POS_LOTHER] = '';
611 24
            $context['currentToken'][Token::POS_LSPACE] = '';
612 24
            return;
613
        }
614
615
        // Handle spacing (standalone tags, partial indent)
616 675
        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));
617
618 675
        if (static::operator($token[Token::POS_OP], $context, $vars)) {
619 405
            return array($raw, $vars);
620
        }
621
622 514
        if (count($vars) == 0) {
623 6
            return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
624
        }
625
626 508
        if (!isset($vars[0])) {
627 1
            return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
628
        }
629
630 507
        $context['usedFeature'][$raw ? 'raw' : 'enc']++;
631
632 507
        foreach ($vars as $var) {
633 507
            if (!isset($var[0]) || ($var[0] === 0)) {
634 64
                if ($context['level'] == 0) {
635 22
                    $context['usedFeature']['rootthis']++;
636
                }
637 507
                $context['usedFeature']['this']++;
638
            }
639
        }
640
641 507
        if (!isset($vars[0][0])) {
642 47
            return array($raw, $vars);
643
        }
644
645 475
        if ($vars[0][0] === 'else') {
646 28
            static::doElse($context, $vars);
647 28
            return array($raw, $vars);
648
        }
649
650 459
        if (!static::helper($context, $vars[0][0])) {
651 350
            static::lookup($context, $vars);
652 350
            static::log($context, $vars);
653
        }
654
655 459
        return array($raw, $vars);
656
    }
657
658
    /**
659
     * Return 1 or larger number when else token detected
660
     *
661
     * @param array<string,array|string|integer> $context current compile context
662
     * @param array<boolean|integer|string|array> $vars parsed arguments list
663
     *
664
     * @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...
665
     */
666 50
    protected static function doElse(&$context, $vars) {
667 50
        if ($context['level'] == 0) {
668 1
            $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
669
        }
670
671 50
        if (isset($vars[1][0]) && (($vars[1][0] === 'if') || ($vars[1][0] === 'unless'))) {
672 8
            $token = $context['currentToken'];
673 8
            $context['currentToken'][Token::POS_RSPACE] = '{{#' . $vars[1][0] . ' ' . preg_replace('/^\\s*else\\s+' . $vars[1][0] . '\\s+/', '', $token[Token::POS_INNERTAG]) . '}}' . $context['currentToken'][Token::POS_RSPACE];
674 8
            $context['elseif'] = true;
675 8
            $context['elselvl'][0]++;
676
        }
677
678 50
        return ++$context['usedFeature']['else'];
679
    }
680
681
    /**
682
     * Return true when this is {{log ...}}
683
     *
684
     * @param array<string,array|string|integer> $context current compile context
685
     * @param array<boolean|integer|string|array> $vars parsed arguments list
686
     *
687
     * @return boolean|null Return true when it is custom helper
688
     */
689 350
    public static function log(&$context, $vars) {
690 350
        if (isset($vars[0][0]) && ($vars[0][0] === 'log')) {
691 3
            if (!$context['flags']['nohbh']) {
692 3
                if (count($vars) < 2) {
693 1
                    $context['error'][] = "No argument after {{log}} !";
694
                }
695 3
                $context['usedFeature']['log']++;
696 3
                return true;
697
            }
698
        }
699 347
    }
700
701
    /**
702
     * Return true when this is {{lookup ...}}
703
     *
704
     * @param array<string,array|string|integer> $context current compile context
705
     * @param array<boolean|integer|string|array> $vars parsed arguments list
706
     *
707
     * @return boolean|null Return true when it is custom helper
708
     */
709 350
    public static function lookup(&$context, $vars) {
710 350
        if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
711 4
            if (!$context['flags']['nohbh']) {
712 4
                if (count($vars) < 2) {
713 1
                    $context['error'][] = "No argument after {{lookup}} !";
714 3
                } else if (count($vars) < 3) {
715 1
                    $context['error'][] = "{{lookup}} requires 2 arguments !";
716
                }
717 4
                $context['usedFeature']['lookup']++;
718 4
                return true;
719
            }
720
        }
721 346
    }
722
723
    /**
724
     * Return true when the name is listed in helper table
725
     *
726
     * @param array<string,array|string|integer> $context current compile context
727
     * @param string $name token name
728
     *
729
     * @return boolean Return true when it is custom helper
730
     */
731 467
    public static function helper(&$context, $name) {
732 467
        if (isset($context['helpers'][$name])) {
733 124
            $context['usedFeature']['helper']++;
734 124
            return true;
735
        }
736
737 352
        return false;
738
    }
739
740
    /**
741
     * detect for block custom helper
742
     *
743
     * @param array<string,array|string|integer> $context current compile context
744
     * @param array<boolean|integer|string|array> $vars parsed arguments list
745
     *
746
     * @return boolean|null Return true when this token is block custom helper
747
     */
748 337
    protected static function isBlockHelper($context, $vars) {
749 337
        if (!isset($vars[0][0])) {
750 4
            return;
751
        }
752
753 334
        if (!isset($context['helpers'][$vars[0][0]])) {
754 282
            return;
755
        }
756
757 58
        return true;
758
    }
759
760
    /**
761
     * validate inline partial
762
     *
763
     * @param array<string,array|string|integer> $context current compile context
764
     * @param array<boolean|integer|string|array> $vars parsed arguments list
765
     *
766
     * @return boolean Return true always
767
     */
768 13
    protected static function inline(&$context, $vars) {
769 13
        if (!$context['flags']['runpart']) {
770 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
771
        }
772 13
        if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
773 1
            $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
774
        }
775 13
        if (!isset($vars[1][0])) {
776 1
            $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
777
        }
778 13
        return true;
779
    }
780
781
    /**
782
     * validate partial
783
     *
784
     * @param array<string,array|string|integer> $context current compile context
785
     * @param array<boolean|integer|string|array> $vars parsed arguments list
786
     *
787
     * @return integer|boolean Return 1 or larger number for runtime partial, return true for other case
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|double|boolean?

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

Loading history...
788
     */
789 95
    protected static function partial(&$context, $vars) {
790 95
        if (Parser::isSubExp($vars[0])) {
791 5
            if ($context['flags']['runpart']) {
792 4
                return $context['usedFeature']['dynpartial']++;
793
            } else {
794 1
                $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
795 1
                return true;
796
            }
797
        } else {
798 90
            if ($context['currentToken'][Token::POS_OP] !== '#>') {
799 85
                Partial::read($context, $vars[0][0]);
800
            }
801
        }
802 90
        if (!$context['flags']['runpart']) {
803 11
        $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
804 11
            if ($named || (count($vars) > 1)) {
805 1
                $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
806
            }
807
        }
808
809 90
        return true;
810
    }
811
812
    /**
813
     * Modify $token when spacing rules matched.
814
     *
815
     * @param array<string> $token detected handlebars {{ }} token
816
     * @param array<string,array|string|integer> $context current compile context
817
     * @param boolean $nost do not do stand alone logic
818
     *
819
     * @return string|null Return compiled code segment for the token
820
     */
821 704
    protected static function spacing(&$token, &$context, $nost = false) {
822
        // left line change detection
823 704
        $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
824 704
        $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
825
        // right line change detection
826 704
        $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
827 704
        $st = true;
828
        // setup ahead flag
829 704
        $ahead = $context['tokens']['ahead'];
830 704
        $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
831
        // reset partial indent
832 704
        $context['tokens']['partialind'] = '';
833
        // same tags in the same line , not standalone
834 704
        if (!$lsp && $ahead) {
835 343
            $st = false;
836
        }
837 704
        if ($nost) {
838 502
            $st = false;
839
        }
840
        // not standalone because other things in the same line ahead
841 704
        if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
842 197
            $st = false;
843
        }
844
        // not standalone because other things in the same line behind
845 704
        if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
846 345
            $st = false;
847
        }
848 704
        if ($st && (($lsp && $rsp) // both side cr
849 126
            || ($rsp && !$token[Token::POS_LOTHER]) // first line without left
850 704
            || ($lsp && !$token[Token::POS_ROTHER]) // final line
851
           )) {
852
            // handle partial
853 63
            if ($token[Token::POS_OP] === '>') {
854 15
                if (!$context['flags']['noind']) {
855 10
                    $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
856 15
                    $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
857
                }
858
            } else {
859 52
                $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
860
            }
861 63
            $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
862
        }
863
864
        // Handle space control.
865 704
        if ($token[Token::POS_LSPACECTL]) {
866 30
            $token[Token::POS_LSPACE] = '';
867
        }
868 704
        if ($token[Token::POS_RSPACECTL]) {
869 33
            $token[Token::POS_RSPACE] = '';
870
        }
871 704
    }
872
}
873
874