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 ( b36705...055742 )
by Zordius
04:55
created

Compiler::blockEnd()   C

Complexity

Conditions 11
Paths 21

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 11.307

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 19
cts 22
cp 0.8636
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 22
nc 21
nop 3
crap 11.307

How to fix   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-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 of LightnCandy Compiler
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
use \LightnCandy\Validator;
24
use \LightnCandy\Token;
25
use \LightnCandy\Expression;
26
use \LightnCandy\Parser;
27
28
/**
29
 * LightnCandy Compiler
30
 */
31
class Compiler extends Validator
32
{
33
    public static $lastParsed;
34
35
    /**
36
     * Compile template into PHP code
37
     *
38
     * @param array<string,array|string|integer> $context Current context
39
     * @param string $template handlebars template
40
     *
41
     * @return string|null generated PHP code
42
     */
43 761
    public static function compileTemplate(&$context, $template) {
44 761
        array_unshift($context['parsed'], array());
45 761
        Validator::verify($context, $template);
46 761
        static::$lastParsed = $context['parsed'];
47
48 761
        if (count($context['error'])) {
49 77
            return;
50
        }
51
52 685
        Parser::setDelimiter($context);
53
54 685
        $context['compile'] = true;
55
56
        // Handle dynamic partials
57 685
        Partial::handleDynamic($context);
58
59
        // Do PHP code generation.
60 685
        $code = '';
61 685
        foreach ($context['parsed'][0] as $info) {
62 685
            if (is_array($info)) {
63 644
                $context['tokens']['current']++;
64 644
                $code .= "'" . static::compileToken($context, $info) . "'";
65
            } else {
66 685
                $code .= $info;
67
            }
68
        }
69
70 685
        array_shift($context['parsed']);
71
72 685
        return $code;
73
    }
74
75
    /**
76
     * Compose LightnCandy render codes for include()
77
     *
78
     * @param array<string,array|string|integer> $context Current context
79
     * @param string $code generated PHP code
80
     *
81
     * @return string Composed PHP code
82
     */
83 684
    public static function composePHPRender($context, $code) {
84 684
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
85 684
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
86 684
        $flagJSLen = Expression::boolString($context['flags']['jslen']);
87 684
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
88 684
        $flagProp = Expression::boolString($context['flags']['prop']);
89 684
        $flagMethod = Expression::boolString($context['flags']['method']);
90 684
        $flagLambda = Expression::boolString($context['flags']['lambda']);
91 684
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
92 684
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
93 684
        $flagEcho = Expression::boolString($context['flags']['echo']);
94 684
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
95 684
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
96
97 684
        $constants = Exporter::constants($context);
98 684
        $helpers = Exporter::helpers($context);
99 684
        $partials = implode(",\n", $context['partialCode']);
100 684
        $debug = Runtime::DEBUG_ERROR_LOG;
101 684
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as {$context['runtimealias']};";
102 684
        $safeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] === 0)) ? "use {$context['safestring']} as SafeString;" : '';
103 684
        $exportSafeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] >0)) ? Exporter::safestring($context) : '';
104
105
        // Return generated PHP code string.
106
        return <<<VAREND
107 684
$safeString{$use}{$exportSafeString}return function (\$in = null, \$options = null) {
108 684
    \$helpers = $helpers;
109 684
    \$partials = array($partials);
110
    \$cx = array(
111
        'flags' => array(
112 684
            'jstrue' => $flagJStrue,
113 684
            'jsobj' => $flagJSObj,
114 684
            'jslen' => $flagJSLen,
115 684
            'spvar' => $flagSPVar,
116 684
            'prop' => $flagProp,
117 684
            'method' => $flagMethod,
118 684
            'lambda' => $flagLambda,
119 684
            'mustlok' => $flagMustlok,
120 684
            'mustlam' => $flagMustlam,
121 684
            'echo' => $flagEcho,
122 684
            'partnc' => $flagPartNC,
123 684
            'knohlp' => $flagKnownHlp,
124 684
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
125
        ),
126 684
        'constants' => $constants,
127
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
128
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
129
        'scopes' => array(),
130
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
131
        'blparam' => array(),
132
        'partialid' => 0,
133 684
        'runtime' => '{$context['runtime']}',
134
    );
135 684
    {$context['renderex']}
136 684
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
137 684
};
138
VAREND
139
        ;
140
    }
141
142
    /**
143
     * Get function name for standalone or none standalone template.
144
     *
145
     * @param array<string,array|string|integer> $context Current context of compiler progress.
146
     * @param string $name base function name
147
     * @param string $tag original handlabars tag for debug
148
     *
149
     * @return string compiled Function name
150
     *
151
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), 'test', ''
152
     * @expect 'LL::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LL'), 'test2', ''
153
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 0, 'funcprefix' => 'lala_abc'), 'test3', ''
154
     * @expect 'RR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'runtimealias' => 'RR', 'funcprefix' => 'haha456'), 'test', 'abc'
155
     */
156 619
    protected static function getFuncName(&$context, $name, $tag) {
157 619
        static::addUsageCount($context, 'runtime', $name);
158
159 619
        if ($context['flags']['debug'] && ($name != 'miss')) {
160 10
            $dbg = "'$tag', '$name', ";
161 10
            $name = 'debug';
162 10
            static::addUsageCount($context, 'runtime', 'debug');
163
        } else {
164 617
            $dbg = '';
165
        }
166
167 619
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "{$context['runtimealias']}::$name($dbg";
168
    }
169
170
    /**
171
     * Get string presentation of variables
172
     *
173
     * @param array<string,array|string|integer> $context current compile context
174
     * @param array<array> $vn variable name array.
175
     * @param array<string>|null $blockParams block param list
176
     *
177
     * @return array<string|array> variable names
178
     *
179
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
180
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
181
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
182
     */
183 278
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
184 278
        $vars = array(array(), array());
185 278
        $exps = array();
186 278
        foreach ($vn as $i => $v) {
187 236
            $V = static::getVariableNameOrSubExpression($context, $v);
188 236
            if (is_string($i)) {
189 37
                $vars[1][] = "'$i'=>{$V[0]}";
190
            } else {
191 222
                $vars[0][] = $V[0];
192
            }
193 236
            $exps[] = $V[1];
194
        }
195 278
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
196 278
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
197
    }
198
199
    /**
200
     * Get string presentation of a sub expression
201
     *
202
     * @param array<string,array|string|integer> $context current compile context
203
     * @param array<boolean|integer|string|array> $vars parsed arguments list
204
     *
205
     * @return array<string> code representing passed expression
206
     */
207 43
    public static function compileSubExpression(&$context, $vars) {
208 43
        $ret = static::customHelper($context, $vars, true, true, true);
209
210 43
        if (($ret === null) && $context['flags']['lambda']) {
211 4
            $ret = static::compileVariable($context, $vars, true, true);
212
        }
213
214 43
        return array($ret ? $ret : '', 'FIXME: $subExpression');
215
    }
216
217
    /**
218
     * Get string presentation of a subexpression or a variable
219
     *
220
     * @param array<array|string|integer> $context current compile context
221
     * @param array<array|string|integer> $var variable parsed path
222
     *
223
     * @return array<string> variable names
0 ignored issues
show
Documentation introduced by
Should the return type not be array<array|string|integer>?

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...
224
     */
225 391
    protected static function getVariableNameOrSubExpression(&$context, $var) {
226 391
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
227
    }
228
229
    /**
230
     * Get string presentation of a variable
231
     *
232
     * @param array<array|string|integer> $var variable parsed path
233
     * @param array<array|string|integer> $context current compile context
234
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
235
     *
236
     * @return array<string> variable names
0 ignored issues
show
Documentation introduced by
Should the return type not be array<array|string|integer>?

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...
237
     *
238
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
239
     * @expect array('((is_array($in) && isset($in[\'true\'])) ? $in[\'true\'] : null)', '[true]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('true')
240
     * @expect array('((is_array($in) && isset($in[\'false\'])) ? $in[\'false\'] : null)', '[false]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('false')
241
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
242
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
243
     * @expect array('((is_array($in) && isset($in[\'2\'])) ? $in[\'2\'] : null)', '[2]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('2')
244
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
245
     * @expect array('((is_array($in) && isset($in[\'@index\'])) ? $in[\'@index\'] : null)', '[@index]') when input array('flags'=>array('spvar'=>false,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('@index')
246
     * @expect array("(isset(\$cx['sp_vars']['index']) ? \$cx['sp_vars']['index'] : null)", '@[index]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('@index')
247
     * @expect array("(isset(\$cx['sp_vars']['key']) ? \$cx['sp_vars']['key'] : null)", '@[key]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('@key')
248
     * @expect array("(isset(\$cx['sp_vars']['first']) ? \$cx['sp_vars']['first'] : null)", '@[first]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('@first')
249
     * @expect array("(isset(\$cx['sp_vars']['last']) ? \$cx['sp_vars']['last'] : null)", '@[last]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('@last')
250
     * @expect array('((is_array($in) && isset($in[\'"a"\'])) ? $in[\'"a"\'] : null)', '["a"]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('"a"')
251
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
252
     * @expect array('((is_array($in) && isset($in[\'a\'])) ? $in[\'a\'] : null)', '[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array('a')
253
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-1]) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-1]) && isset($cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\'])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\'] : null)', '../[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array(1,'a')
254
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-3]) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-3]) && isset($cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\'])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\'] : null)', '../../../[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array(3,'a')
255
     * @expect array('((is_array($in) && isset($in[\'id\'])) ? $in[\'id\'] : null)', 'this.[id]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0)), array(null, 'id')
256
     * @expect array('LR::v($cx, $in, isset($in) ? $in : null, array(\'id\'))', 'this.[id]') when input array('flags'=>array('prop'=>true,'spvar'=>true,'debug'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0,'lambda'=>0,'jslen'=>0,'standalone'=>0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), array(null, 'id')
257
     */
258 612
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
259 612
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
260 88
            if ($var[1] === "undefined") {
261 2
                $var[1] = "null";
262
            }
263 88
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
264
        }
265
266 571
        list($levels, $spvar, $var) = Expression::analyze($context, $var);
0 ignored issues
show
Documentation introduced by
$var is of type array<integer,array|stri...array|string|integer"}>, but the function expects a array<integer,array|string|integer>.

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...
267 571
        $exp = Expression::toString($levels, $spvar, $var);
268 571
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
269
270
        // change base when trace to parent
271 571
        if ($levels > 0) {
272 41
            if ($spvar) {
273 2
                $base .= str_repeat("['_parent']", $levels);
274
            } else {
275 39
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
276
            }
277
        }
278
279 571
        if (((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) && ($lookup === null)) {
280 148
            return array($base, $exp);
281
        }
282
283 510
        if ((count($var) > 0) && ($var[0] === null)) {
284 1
            array_shift($var);
285
        }
286
287
        // To support recursive context lookup, instance properties + methods and lambdas
288
        // the only way is using slower rendering time variable resolver.
289 510
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
290 387
            $L = Expression::listString($var);
291 387
            $L = ($L === '') ? array() : array($L);
292 387
            if ($lookup) {
293 3
                $L[] = $lookup[0];
294
            }
295 387
            $A = $args ? ",$args[0]" : '';
296 387
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
297 387
            $V = Expression::listString($var);
0 ignored issues
show
Unused Code introduced by
$V is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
298 387
            return array(static::getFuncName($context, 'v', $exp) . "\$cx, \$in, isset($base) ? $base : null, array(" . implode($L, ',') . ")$A)", $lookup ? "lookup $exp $lookup[1]" : "$exp$E");
299
        }
300
301 124
        $n = Expression::arrayString($var);
302 124
        $k = array_pop($var);
303 124
        $L = $lookup ? "[{$lookup[0]}]" : '';
304 124
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
305
306 124
        $checks = array();
307 124
        if ($levels > 0) {
308 11
            $checks[] = "isset($base)";
309
        }
310 124
        if (!$spvar) {
311 120
            if (($levels === 0) && $p) {
312 13
                $checks[] = "isset($base$p)";
313
            }
314 120
            $checks[] = "is_array($base$p)";
315
        }
316 124
        $checks[] = "isset($base$n$L)";
317 124
        $check = ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '');
318
319 124
        $lenStart = '';
320 124
        $lenEnd = '';
321
322 124
        if ($context['flags']['jslen']) {
323 50
            if (($lookup === null) && ($k === 'length')) {
324 1
                array_pop($checks);
325 1
                $lenStart = '(' . ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '') . " ? count($base" . Expression::arrayString($var) . ') : ';
326 1
                $lenEnd = ')';
327
            }
328
        }
329
330 124
        return array("($check ? $base$n$L : $lenStart" . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ")$lenEnd", $lookup ? "lookup $exp $lookup[1]" : $exp);
331
    }
332
333
    /**
334
     * Return compiled PHP code for a handlebars token
335
     *
336
     * @param array<string,array|string|integer> $context current compile context
337
     * @param array<string,array|boolean> $info parsed information
338
     *
339
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer|double|string?

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...
340
     */
341 644
    protected static function compileToken(&$context, $info) {
342 644
        list($raw, $vars, $token, $indent) = $info;
343
344 644
        $context['tokens']['partialind'] = $indent;
345 644
        $context['currentToken'] = $token;
346
347 644
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
348 397
            return $ret;
349
        }
350
351 502
        if (isset($vars[0][0])) {
352 468
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
353 118
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
354
            }
355 357
            if ($context['flags']['else'] && ($vars[0][0] === 'else')) {
356 35
                return static::doElse($context, $vars);
357
            }
358 333
            if ($vars[0][0] === 'lookup') {
359 5
                return static::compileLookup($context, $vars, $raw);
360
            }
361 328
            if ($vars[0][0] === 'log') {
362 2
                return static::compileLog($context, $vars, $raw);
363
            }
364
        }
365
366 368
        return static::compileVariable($context, $vars, $raw, false);
367
    }
368
369
    /**
370
     * handle partial
371
     *
372
     * @param array<string,array|string|integer> $context current compile context
373
     * @param array<boolean|integer|string|array> $vars parsed arguments list
374
     *
375
     * @return string Return compiled code segment for the partial
376
     */
377 97
    public static function partial(&$context, $vars) {
378 97
        Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|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...
379 97
        $pid = Parser::getPartialBlock($vars);
380 97
        $p = array_shift($vars);
381 97
        if ($context['flags']['runpart']) {
382 88
            if (!isset($vars[0])) {
383 81
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
384
            }
385 88
            $v = static::getVariableNames($context, $vars);
386 88
            $tag = ">$p[0] " .implode(' ', $v[1]);
387 88
            if (Parser::isSubExp($p)) {
388 5
                list($p) = static::compileSubExpression($context, $p[1]);
389
            } else {
390 83
                $p = "'$p[0]'";
391
            }
392 88
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
393 88
            return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, $p, $v[0],$pid$sp){$context['ops']['seperator']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...['ops']['seperator']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::partial of type integer|double|boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
394
        }
395 9
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
396
    }
397
398
    /**
399
     * handle inline partial
400
     *
401
     * @param array<string,array|string|integer> $context current compile context
402
     * @param array<boolean|integer|string|array> $vars parsed arguments list
403
     *
404
     * @return string Return compiled code segment for the partial
405
     */
406 11
    public static function inline(&$context, $vars) {
407 11
        Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|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...
408 11
        list($code) = array_shift($vars);
409 11
        $p = array_shift($vars);
410 11
        if (!isset($vars[0])) {
411 11
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
412
        }
413 11
        $v = static::getVariableNames($context, $vars);
414 11
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
415 11
        return $context['ops']['seperator'] . static::getFuncName($context, 'in', $tag) . "\$cx, '{$p[0]}', $code){$context['ops']['seperator']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...['ops']['seperator']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::inline of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
416
    }
417
418
    /**
419
     * Return compiled PHP code for a handlebars inverted section begin token
420
     *
421
     * @param array<string,array|string|integer> $context current compile context
422
     * @param array<boolean|integer|string|array> $vars parsed arguments list
423
     *
424
     * @return string Return compiled code segment for the token
425
     */
426 38
    protected static function invertedSection(&$context, $vars) {
427 38
        $v = static::getVariableName($context, $vars[0]);
428 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
429
    }
430
431
    /**
432
     * Return compiled PHP code for a handlebars block custom helper begin token
433
     *
434
     * @param array<string,array|string|integer> $context current compile context
435
     * @param array<boolean|integer|string|array> $vars parsed arguments list
436
     * @param boolean $inverted the logic will be inverted
437
     *
438
     * @return string Return compiled code segment for the token
439
     */
440 63
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
441 63
        $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|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...
442 63
        $ch = array_shift($vars);
443 63
        $inverted = $inverted ? 'true' : 'false';
444 63
        static::addUsageCount($context, 'helpers', $ch[0]);
445 63
        $v = static::getVariableNames($context, $vars, $bp);
446
447 63
        return $context['ops']['seperator'] . static::getFuncName($context, 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}";
448
    }
449
450
    /**
451
     * Return compiled PHP code for a handlebars block end token
452
     *
453
     * @param array<string,array|string|integer> $context current compile context
454
     * @param array<boolean|integer|string|array> $vars parsed arguments list
455
     * @param string|null $matchop should also match to this operator
456
     *
457
     * @return string Return compiled code segment for the token
458
     */
459 332
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
460 332
        $pop = $context['stack'][count($context['stack']) - 1];
461
462 332
        switch (isset($context['helpers'][$context['currentToken'][Token::POS_INNERTAG]]) ? 'skip' : $context['currentToken'][Token::POS_INNERTAG]) {
463 332
            case 'if':
464 275
            case 'unless':
465 83
                if ($pop === ':') {
466 30
                    array_pop($context['stack']);
467 30
                    return "{$context['ops']['cnd_end']}";
468
                }
469 57
                if (!$context['flags']['nohbh']) {
470 55
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
471
                }
472 2
                break;
473 271
            case 'with':
474 31
                if (!$context['flags']['nohbh']) {
475 30
                    return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
476
                }
477
        }
478
479 250
        if ($pop === ':') {
480
            array_pop($context['stack']);
481
            return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
482
        }
483
484
        switch($pop) {
485 250
            case '#':
486 225
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
487 33
            case '^':
488 33
                return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
489
        }
490
    }
491
492
    /**
493
     * Return compiled PHP code for a handlebars block begin token
494
     *
495
     * @param array<string,array|string|integer> $context current compile context
496
     * @param array<boolean|integer|string|array> $vars parsed arguments list
497
     *
498
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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...
499
     */
500 248
    protected static function blockBegin(&$context, $vars) {
501 248
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
502 248
        if (!$context['flags']['nohbh']) {
503 210
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
504 210
                case 'if':
505 73
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
506 73
                    return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]}, {$includeZero})){$context['ops']['cnd_then']}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug Best Practice introduced by
The return type of return "{$context['ops']...t['ops']['cnd_then']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
507 156
                case 'unless':
508 4
                    return "{$context['ops']['cnd_start']}(!" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]}, false)){$context['ops']['cnd_then']}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug Best Practice introduced by
The return type of return "{$context['ops']...t['ops']['cnd_then']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
509 153
                case 'each':
510 50
                    return static::section($context, $vars, true);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, 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...
Bug Compatibility introduced by
The expression static::section($context, $vars, true); of type string|null adds the type string to the return on line 510 which is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.
Loading history...
511 106
                case 'with':
512 30
                    if ($r = static::with($context, $vars)) {
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, 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...
513 30
                        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $r; (string) is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
514
                    }
515
            }
516
        }
517
518 116
        return static::section($context, $vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, 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...
Bug Compatibility introduced by
The expression static::section($context, $vars); of type string|null adds the type string to the return on line 518 which is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.
Loading history...
519
    }
520
521
    /**
522
     * compile {{#foo}} token
523
     *
524
     * @param array<string,array|string|integer> $context current compile context
525
     * @param array<boolean|integer|string|array> $vars parsed arguments list
526
     * @param boolean $isEach the section is #each
527
     *
528
     * @return string|null Return compiled code segment for the token
529
     */
530 166
    protected static function section(&$context, $vars, $isEach = false) {
531 166
        $bs = 'null';
532 166
        $be = '';
533 166
        if ($isEach) {
534 50
            $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|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...
535 50
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
536 50
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
537 50
            array_shift($vars);
538
        }
539 166
        if ($context['flags']['lambda'] && !$isEach) {
540 69
            $V = array_shift($vars);
541 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
542
        } else {
543 97
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
544
        }
545 166
        $each = $isEach ? 'true' : 'false';
546 166
        return $context['ops']['seperator'] . static::getFuncName($context, 'sec', ($isEach ? 'each ' : '') . $v[1] . $be) . "\$cx, {$v[0]}, $bs, \$in, $each, function(\$cx, \$in) {{$context['ops']['f_start']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...xt['ops']['f_start']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::section of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
547
    }
548
549
    /**
550
     * compile {{with}} token
551
     *
552
     * @param array<string,array|string|integer> $context current compile context
553
     * @param array<boolean|integer|string|array> $vars parsed arguments list
554
     *
555
     * @return string|null Return compiled code segment for the token
556
     */
557 30
    protected static function with(&$context, $vars) {
558 30
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
559 30
        $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, but the function expects a array<integer,boolean|integer|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...
560 30
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
561 30
        $be = $bp ? " as |$bp[0]|" : '';
562 30
        return $context['ops']['seperator'] . static::getFuncName($context, 'wi', 'with ' . $v[1] . $be) . "\$cx, {$v[0]}, $bs, \$in, function(\$cx, \$in) {{$context['ops']['f_start']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...xt['ops']['f_start']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::with of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
563
    }
564
565
    /**
566
     * Return compiled PHP code for a handlebars custom helper token
567
     *
568
     * @param array<string,array|string|integer> $context current compile context
569
     * @param array<boolean|integer|string|array> $vars parsed arguments list
570
     * @param boolean $raw is this {{{ token or not
571
     * @param boolean $nosep true to compile without seperator
572
     * @param boolean $subExp true when compile for subexpression
573
     *
574
     * @return string|null Return compiled code segment for the token when the token is custom helper
575
     */
576 477
    protected static function customHelper(&$context, $vars, $raw, $nosep, $subExp = false) {
577 477
        if (count($vars[0]) > 1) {
578 71
            return;
579
        }
580
581 426
        if (!isset($context['helpers'][$vars[0][0]])) {
582 308
            if ($subExp) {
583 6
                if ($vars[0][0] == 'lookup') {
584 2
                    return static::compileLookup($context, $vars, $raw, true);
585
                }
586
            }
587 306
            return;
588
        }
589
590 129
        $fn = $raw ? 'raw' : $context['ops']['enc'];
591 129
        $ch = array_shift($vars);
592 129
        $v = static::getVariableNames($context, $vars);
593 129
        static::addUsageCount($context, 'helpers', $ch[0]);
594 129
        $sep = $nosep ? '' : $context['ops']['seperator'];
595
596 129
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
597
    }
598
599
    /**
600
     * Return compiled PHP code for a handlebars else token
601
     *
602
     * @param array<string,array|string|integer> $context current compile context
603
     * @param array<boolean|integer|string|array> $vars parsed arguments list
604
     *
605
     * @return string Return compiled code segment for the token when the token is else
606
     */
607 57
    protected static function doElse(&$context, $vars) {
608 57
        $v = $context['stack'][count($context['stack']) - 2];
609
610 57
        if ((($v === '[if]') && !isset($context['helpers']['if'])) ||
611 57
           (($v === '[unless]') && !isset($context['helpers']['unless']))) {
612 30
           $context['stack'][] = ':';
613 30
           return "{$context['ops']['cnd_else']}";
614
        }
615
616 30
        return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
617
    }
618
619
    /**
620
     * Return compiled PHP code for a handlebars log token
621
     *
622
     * @param array<string,array|string|integer> $context current compile context
623
     * @param array<boolean|integer|string|array> $vars parsed arguments list
624
     * @param boolean $raw is this {{{ token or not
625
     *
626
     * @return string Return compiled code segment for the token
627
     */
628 2
    protected static function compileLog(&$context, &$vars, $raw) {
0 ignored issues
show
Unused Code introduced by
The parameter $raw is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
629 2
        array_shift($vars);
630 2
        $v = static::getVariableNames($context, $vars);
631 2
        return $context['ops']['seperator'] . static::getFuncName($context, 'lo', $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}";
0 ignored issues
show
Documentation introduced by
$v[1] is of type array, but the function expects a string.

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...
632
    }
633
634
    /**
635
     * Return compiled PHP code for a handlebars lookup token
636
     *
637
     * @param array<string,array|string|integer> $context current compile context
638
     * @param array<boolean|integer|string|array> $vars parsed arguments list
639
     * @param boolean $raw is this {{{ token or not
640
     * @param boolean $nosep true to compile without seperator
641
     *
642
     * @return string Return compiled code segment for the token
643
     */
644 7
    protected static function compileLookup(&$context, &$vars, $raw, $nosep = false) {
645 7
        $v2 = static::getVariableName($context, $vars[2]);
646 7
        $v = static::getVariableName($context, $vars[1], $v2);
0 ignored issues
show
Documentation introduced by
$v2 is of type array<integer,array|stri...rray<integer,string>"}>, but the function expects a array<integer,string>|null.

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...
647 7
        $sep = $nosep ? '' : $context['ops']['seperator'];
648 7
        $ex = $nosep ? ', 1' : '';
649
650 7
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
651 6
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}$ex){$sep}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array<integer,string>; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
652
        } else {
653 1
            return $raw ? "{$sep}$v[0]{$sep}" : "{$sep}htmlspecialchars((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$sep}";
654
        }
655
    }
656
657
    /**
658
     * Return compiled PHP code for template output
659
     *
660
     * @param array<string,array|string|integer> $context current compile context
661
     * @param string $variable PHP code for the variable
662
     * @param string $expression normalized handlebars expression
663
     * @param boolean $raw is this {{{ token or not
664
     * @param boolean $nosep true to compile without seperator
665
     *
666
     * @return string Return compiled code segment for the token
667
     */
668 477
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
669 477
        $sep = $nosep ? '' : $context['ops']['seperator'];
670 477
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
671 429
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
672
        } else {
673 48
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlspecialchars((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
674
        }
675
    }
676
677
    /**
678
     * Return compiled PHP code for a handlebars variable token
679
     *
680
     * @param array<string,array|string|integer> $context current compile context
681
     * @param array<boolean|integer|string|array> $vars parsed arguments list
682
     * @param boolean $raw is this {{{ token or not
683
     * @param boolean $nosep true to compile without seperator
684
     *
685
     * @return string Return compiled code segment for the token
686
     */
687 371
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
688 371
        if ($context['flags']['lambda']) {
689 226
            $V = array_shift($vars);
690 226
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
691
        } else {
692 145
            $v = static::getVariableName($context, $vars[0]);
693
        }
694 371
        return static::compileOutput($context, $v[0], $v[1], $raw, $nosep);
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array<integer,string>; however, LightnCandy\Compiler::compileOutput() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
695
    }
696
697
    /**
698
     * Add usage count to context
699
     *
700
     * @param array<string,array|string|integer> $context current context
701
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
702
     * @param string $name used name
703
     * @param integer $count increment
704
     *
705
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
706
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
707
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
708
     */
709 619
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
710 619
        if (!isset($context['usedCount'][$category][$name])) {
711 619
            $context['usedCount'][$category][$name] = 0;
712
        }
713 619
        return ($context['usedCount'][$category][$name] += $count);
714
    }
715
}
716
717