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 ( fe9054...71abc9 )
by Zordius
02:39
created

Compiler::addUsageCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 4
crap 2
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 749
    public static function compileTemplate(&$context, $template) {
44 749
        array_unshift($context['parsed'], array());
45 749
        Validator::verify($context, $template);
46 749
        static::$lastParsed = $context['parsed'];
47
48 749
        if (count($context['error'])) {
49 77
            return;
50
        }
51
52 673
        Parser::setDelimiter($context);
53
54 673
        $context['compile'] = true;
55
56
        // Handle dynamic partials
57 673
        Partial::handleDynamic($context);
58
59
        // Do PHP code generation.
60 673
        $code = '';
61 673
        foreach ($context['parsed'][0] as $info) {
62 673
            if (is_array($info)) {
63 632
                $context['tokens']['current']++;
64 632
                $code .= "'" . static::compileToken($context, $info) . "'";
65
            } else {
66 673
                $code .= $info;
67
            }
68
        }
69
70 673
        array_shift($context['parsed']);
71
72 673
        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 672
    public static function composePHPRender($context, $code) {
84 672
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
85 672
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
86 672
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
87 672
        $flagProp = Expression::boolString($context['flags']['prop']);
88 672
        $flagMethod = Expression::boolString($context['flags']['method']);
89 672
        $flagLambda = Expression::boolString($context['flags']['lambda']);
90 672
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
91 672
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
92 672
        $flagEcho = Expression::boolString($context['flags']['echo']);
93 672
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
94 672
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
95
96 672
        $constants = Exporter::constants($context);
97 672
        $helpers = Exporter::helpers($context);
98 672
        $partials = implode(",\n", $context['partialCode']);
99 672
        $debug = Runtime::DEBUG_ERROR_LOG;
100 672
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as {$context['runtimealias']};";
101 672
        $safeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] === 0)) ? "use {$context['safestring']} as SafeString;" : '';
102 672
        $exportSafeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] >0)) ? Exporter::safestring($context) : '';
103
104
        // Return generated PHP code string.
105
        return <<<VAREND
106 672
$safeString{$use}{$exportSafeString}return function (\$in = null, \$options = null) {
107 672
    \$helpers = $helpers;
108 672
    \$partials = array($partials);
109
    \$cx = array(
110
        'flags' => array(
111 672
            'jstrue' => $flagJStrue,
112 672
            'jsobj' => $flagJSObj,
113 672
            'spvar' => $flagSPVar,
114 672
            'prop' => $flagProp,
115 672
            'method' => $flagMethod,
116 672
            'lambda' => $flagLambda,
117 672
            'mustlok' => $flagMustlok,
118 672
            'mustlam' => $flagMustlam,
119 672
            'echo' => $flagEcho,
120 672
            'partnc' => $flagPartNC,
121 672
            'knohlp' => $flagKnownHlp,
122 672
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
123
        ),
124 672
        'constants' => $constants,
125
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
126
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
127
        'scopes' => array(),
128
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
129
        'blparam' => array(),
130
        'partialid' => 0,
131 672
        'runtime' => '{$context['runtime']}',
132
    );
133 672
    {$context['renderex']}
134 672
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
135 672
};
136
VAREND
137
        ;
138
    }
139
140
    /**
141
     * Get function name for standalone or none standalone template.
142
     *
143
     * @param array<string,array|string|integer> $context Current context of compiler progress.
144
     * @param string $name base function name
145
     * @param string $tag original handlabars tag for debug
146
     *
147
     * @return string compiled Function name
148
     *
149
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), 'test', ''
150
     * @expect 'LL::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LL'), 'test2', ''
151
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 0, 'funcprefix' => 'lala_abc'), 'test3', ''
152
     * @expect 'RR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'runtimealias' => 'RR', 'funcprefix' => 'haha456'), 'test', 'abc'
153
     */
154 607
    protected static function getFuncName(&$context, $name, $tag) {
155 607
        static::addUsageCount($context, 'runtime', $name);
156
157 607
        if ($context['flags']['debug'] && ($name != 'miss')) {
158 10
            $dbg = "'$tag', '$name', ";
159 10
            $name = 'debug';
160 10
            static::addUsageCount($context, 'runtime', 'debug');
161
        } else {
162 605
            $dbg = '';
163
        }
164
165 607
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "{$context['runtimealias']}::$name($dbg";
166
    }
167
168
    /**
169
     * Get string presentation of variables
170
     *
171
     * @param array<string,array|string|integer> $context current compile context
172
     * @param array<array> $vn variable name array.
173
     * @param array<string>|null $blockParams block param list
174
     *
175
     * @return array<string|array> variable names
176
     *
177
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
178
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
179
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
180
     */
181 272
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
182 272
        $vars = array(array(), array());
183 272
        $exps = array();
184 272
        foreach ($vn as $i => $v) {
185 230
            $V = static::getVariableNameOrSubExpression($context, $v);
186 230
            if (is_string($i)) {
187 37
                $vars[1][] = "'$i'=>{$V[0]}";
188
            } else {
189 216
                $vars[0][] = $V[0];
190
            }
191 230
            $exps[] = $V[1];
192
        }
193 272
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
194 272
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
195
    }
196
197
    /**
198
     * Get string presentation of a sub expression
199
     *
200
     * @param array<string,array|string|integer> $context current compile context
201
     * @param array<boolean|integer|string|array> $vars parsed arguments list
202
     *
203
     * @return array<string> code representing passed expression
204
     */
205 42
    public static function compileSubExpression(&$context, $vars) {
206 42
        $ret = static::customHelper($context, $vars, true, true, true);
207
208 42
        if (($ret === null) && $context['flags']['lambda']) {
209 4
            $ret = static::compileVariable($context, $vars, true, true);
210
        }
211
212 42
        return array($ret ? $ret : '', 'FIXME: $subExpression');
213
    }
214
215
    /**
216
     * Get string presentation of a subexpression or a variable
217
     *
218
     * @param array<array|string|integer> $context current compile context
219
     * @param array<array|string|integer> $var variable parsed path
220
     *
221
     * @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...
222
     */
223 383
    protected static function getVariableNameOrSubExpression(&$context, $var) {
224 383
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
225
    }
226
227
    /**
228
     * Get string presentation of a variable
229
     *
230
     * @param array<array|string|integer> $var variable parsed path
231
     * @param array<array|string|integer> $context current compile context
232
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
233
     *
234
     * @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...
235
     *
236
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
237
     * @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')
238
     * @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')
239
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
240
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
241
     * @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')
242
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
243
     * @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')
244
     * @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')
245
     * @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')
246
     * @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')
247
     * @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')
248
     * @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"')
249
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
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('((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')
252
     * @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')
253
     * @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')
254
     * @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')
255
     */
256 600
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
257 600
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
258 84
            if ($var[1] === "undefined") {
259 2
                $var[1] = "null";
260
            }
261 84
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
262
        }
263
264 559
        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...
265 559
        $exp = Expression::toString($levels, $spvar, $var);
266 559
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
267
268
        // change base when trace to parent
269 559
        if ($levels > 0) {
270 40
            if ($spvar) {
271 2
                $base .= str_repeat("['_parent']", $levels);
272
            } else {
273 38
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
274
            }
275
        }
276
277 559
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
278 142
            return array($base, $exp);
279
        }
280
281 502
        if ($var[0] === null) {
282 1
            array_shift($var);
283
        }
284
285
        // To support recursive context lookup, instance properties + methods and lambdas
286
        // the only way is using slower rendering time variable resolver.
287 502
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
288 386
            $L = $lookup ? ", $lookup[0]" : '';
289 386
            $A = $args ? ",$args[0]" : '';
290 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
291 386
            return array(static::getFuncName($context, 'v', $exp) . "\$cx, \$in, isset($base) ? $base : null, array(" . Expression::listString($var) . "$L)$A)", $lookup ? "lookup $exp $lookup[1]" : "$exp$E");
292
        }
293
294 117
        $n = Expression::arrayString($var);
295 117
        $k = array_pop($var);
296 117
        $L = $lookup ? "[{$lookup[0]}]" : '';
297 117
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
298
299 117
        $checks = array();
300 117
        if ($levels > 0) {
301 10
            $checks[] = "isset($base)";
302
        }
303 117
        if (!$spvar) {
304 114
            if (($levels === 0) && $p) {
305 11
                $checks[] = "isset($base$p)";
306
            }
307 114
            $checks[] = "is_array($base$p)";
308
        }
309 117
        $checks[] = "isset($base$n$L)";
310 117
        $check = ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '');
311
312 117
        $lenStart = '';
313 117
        $lenEnd = '';
314
315 117
        if ($context['flags']['jslen']) {
316 50
            if (($lookup === null) && ($k === 'length')) {
317 1
                array_pop($checks);
318 1
                $lenStart = '(' . ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '') . " ? count($base" . Expression::arrayString($var) . ') : ';
319 1
                $lenEnd = ')';
320
            }
321
        }
322
323 117
        return array("($check ? $base$n$L : $lenStart" . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ")$lenEnd", $lookup ? "lookup $exp $lookup[1]" : $exp);
324
    }
325
326
    /**
327
     * Return compiled PHP code for a handlebars token
328
     *
329
     * @param array<string,array|string|integer> $context current compile context
330
     * @param array<string,array|boolean> $info parsed information
331
     *
332
     * @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...
333
     */
334 632
    protected static function compileToken(&$context, $info) {
335 632
        list($raw, $vars, $token, $indent) = $info;
336
337 632
        $context['tokens']['partialind'] = $indent;
338 632
        $context['currentToken'] = $token;
339
340 632
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
341 390
            return $ret;
342
        }
343
344 492
        if (isset($vars[0][0])) {
345 459
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
346 116
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
347
            }
348 350
            if ($context['flags']['else'] && ($vars[0][0] === 'else')) {
349 34
                return static::doElse($context, $vars);
350
            }
351 327
            if ($vars[0][0] === 'lookup') {
352 2
                return static::compileLookup($context, $vars, $raw);
353
            }
354 325
            if ($vars[0][0] === 'log') {
355 2
                return static::compileLog($context, $vars, $raw);
356
            }
357
        }
358
359 364
        return static::compileVariable($context, $vars, $raw, false);
360
    }
361
362
    /**
363
     * handle partial
364
     *
365
     * @param array<string,array|string|integer> $context current compile context
366
     * @param array<boolean|integer|string|array> $vars parsed arguments list
367
     *
368
     * @return string Return compiled code segment for the partial
369
     */
370 92
    public static function partial(&$context, $vars) {
371 92
        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...
372 92
        $pid = Parser::getPartialBlock($vars);
373 92
        $p = array_shift($vars);
374 92
        if ($context['flags']['runpart']) {
375 83
            if (!isset($vars[0])) {
376 76
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
377
            }
378 83
            $v = static::getVariableNames($context, $vars);
379 83
            $tag = ">$p[0] " .implode(' ', $v[1]);
380 83
            if (Parser::isSubExp($p)) {
381 5
                list($p) = static::compileSubExpression($context, $p[1]);
382
            } else {
383 78
                $p = "'$p[0]'";
384
            }
385 83
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
386 83
            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...
387
        }
388 9
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
389
    }
390
391
    /**
392
     * handle inline partial
393
     *
394
     * @param array<string,array|string|integer> $context current compile context
395
     * @param array<boolean|integer|string|array> $vars parsed arguments list
396
     *
397
     * @return string Return compiled code segment for the partial
398
     */
399 10
    public static function inline(&$context, $vars) {
400 10
        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...
401 10
        list($code) = array_shift($vars);
402 10
        $p = array_shift($vars);
403 10
        if (!isset($vars[0])) {
404 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
405
        }
406 10
        $v = static::getVariableNames($context, $vars);
407 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
408 10
        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...
409
    }
410
411
    /**
412
     * Return compiled PHP code for a handlebars inverted section begin token
413
     *
414
     * @param array<string,array|string|integer> $context current compile context
415
     * @param array<boolean|integer|string|array> $vars parsed arguments list
416
     *
417
     * @return string Return compiled code segment for the token
418
     */
419 38
    protected static function invertedSection(&$context, $vars) {
420 38
        $v = static::getVariableName($context, $vars[0]);
421 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
422
    }
423
424
    /**
425
     * Return compiled PHP code for a handlebars block custom helper begin token
426
     *
427
     * @param array<string,array|string|integer> $context current compile context
428
     * @param array<boolean|integer|string|array> $vars parsed arguments list
429
     * @param boolean $inverted the logic will be inverted
430
     *
431
     * @return string Return compiled code segment for the token
432
     */
433 63
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
434 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...
435 63
        $ch = array_shift($vars);
436 63
        $inverted = $inverted ? 'true' : 'false';
437 63
        static::addUsageCount($context, 'helpers', $ch[0]);
438 63
        $v = static::getVariableNames($context, $vars, $bp);
439
440 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']}";
441
    }
442
443
    /**
444
     * Return compiled PHP code for a handlebars block end token
445
     *
446
     * @param array<string,array|string|integer> $context current compile context
447
     * @param array<boolean|integer|string|array> $vars parsed arguments list
448
     * @param string|null $matchop should also match to this operator
449
     *
450
     * @return string Return compiled code segment for the token
451
     */
452 329
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
453 329
        $pop = $context['stack'][count($context['stack']) - 1];
454
455 329
        switch (isset($context['helpers'][$context['currentToken'][Token::POS_INNERTAG]]) ? 'skip' : $context['currentToken'][Token::POS_INNERTAG]) {
456 329
            case 'if':
457 273
            case 'unless':
458 82
                if ($pop === ':') {
459 29
                    array_pop($context['stack']);
460 29
                    return "{$context['ops']['cnd_end']}";
461
                }
462 56
                if (!$context['flags']['nohbh']) {
463 54
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
464
                }
465 2
                break;
466 269
            case 'with':
467 30
                if (!$context['flags']['nohbh']) {
468 29
                    return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
469
                }
470
        }
471
472 248
        if ($pop === ':') {
473
            array_pop($context['stack']);
474
            return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
475
        }
476
477
        switch($pop) {
478 248
            case '#':
479 223
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
480 33
            case '^':
481 33
                return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
482
        }
483
    }
484
485
    /**
486
     * Return compiled PHP code for a handlebars block begin token
487
     *
488
     * @param array<string,array|string|integer> $context current compile context
489
     * @param array<boolean|integer|string|array> $vars parsed arguments list
490
     *
491
     * @return string Return compiled code segment for the token
492
     */
493 245
    protected static function blockBegin(&$context, $vars) {
494 245
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
495 245
        if (!$context['flags']['nohbh']) {
496 207
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
497 207
                case 'if':
498 72
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
499 72
                    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...
500 154
                case 'unless':
501 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...
502 151
                case 'each':
503 48
                    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 Best Practice introduced by
The return type of return static::section($context, $vars, true); (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...
504 105
                case 'with':
505 29
                    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...
506 29
                        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...
507
                    }
508
            }
509
        }
510
511 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 Best Practice introduced by
The return type of return static::section($context, $vars); (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...
512
    }
513
514
    /**
515
     * compile {{#foo}} token
516
     *
517
     * @param array<string,array|string|integer> $context current compile context
518
     * @param array<boolean|integer|string|array> $vars parsed arguments list
519
     * @param boolean $isEach the section is #each
520
     *
521
     * @return string|null Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
522
     */
523 164
    protected static function section(&$context, $vars, $isEach = false) {
524 164
        $bs = 'null';
525 164
        $be = '';
526 164
        if ($isEach) {
527 48
            $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...
528 48
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
529 48
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
530 48
            array_shift($vars);
531
        }
532 164
        if ($context['flags']['lambda'] && !$isEach) {
533 69
            $V = array_shift($vars);
534 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
535
        } else {
536 95
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
537
        }
538 164
        $each = $isEach ? 'true' : 'false';
539 164
        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...
540
    }
541
542
    /**
543
     * compile {{with}} token
544
     *
545
     * @param array<string,array|string|integer> $context current compile context
546
     * @param array<boolean|integer|string|array> $vars parsed arguments list
547
     *
548
     * @return string|null Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
549
     */
550 29
    protected static function with(&$context, $vars) {
551 29
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
552 29
        $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...
553 29
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
554 29
        $be = $bp ? " as |$bp[0]|" : '';
555 29
        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...
556
    }
557
558
    /**
559
     * Return compiled PHP code for a handlebars custom helper token
560
     *
561
     * @param array<string,array|string|integer> $context current compile context
562
     * @param array<boolean|integer|string|array> $vars parsed arguments list
563
     * @param boolean $raw is this {{{ token or not
564
     * @param boolean $nosep true to compile without seperator
565
     * @param boolean $subExp true when compile for subexpression
566
     *
567
     * @return string|null Return compiled code segment for the token when the token is custom helper
568
     */
569 468
    protected static function customHelper(&$context, $vars, $raw, $nosep, $subExp = false) {
570 468
        if (!isset($context['helpers'][$vars[0][0]])) {
571 354
            if ($subExp) {
572 5
                if ($vars[0][0] == 'lookup') {
573 1
                    return static::compileLookup($context, $vars, $raw, true);
574
                }
575
            }
576 353
            return;
577
        }
578
579 127
        $fn = $raw ? 'raw' : $context['ops']['enc'];
580 127
        $ch = array_shift($vars);
581 127
        $v = static::getVariableNames($context, $vars);
582 127
        static::addUsageCount($context, 'helpers', $ch[0]);
583 127
        $sep = $nosep ? '' : $context['ops']['seperator'];
584
585 127
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
586
    }
587
588
    /**
589
     * Return compiled PHP code for a handlebars else token
590
     *
591
     * @param array<string,array|string|integer> $context current compile context
592
     * @param array<boolean|integer|string|array> $vars parsed arguments list
593
     *
594
     * @return string Return compiled code segment for the token when the token is else
595
     */
596 56
    protected static function doElse(&$context, $vars) {
597 56
        $v = $context['stack'][count($context['stack']) - 2];
598
599 56
        if ((($v === '[if]') && !isset($context['helpers']['if'])) ||
600 56
           (($v === '[unless]') && !isset($context['helpers']['unless']))) {
601 29
           $context['stack'][] = ':';
602 29
           return "{$context['ops']['cnd_else']}";
603
        }
604
605 30
        return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
606
    }
607
608
    /**
609
     * Return compiled PHP code for a handlebars log token
610
     *
611
     * @param array<string,array|string|integer> $context current compile context
612
     * @param array<boolean|integer|string|array> $vars parsed arguments list
613
     * @param boolean $raw is this {{{ token or not
614
     *
615
     * @return string Return compiled code segment for the token
616
     */
617 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...
618 2
        array_shift($vars);
619 2
        $v = static::getVariableNames($context, $vars);
620 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...
621
    }
622
623
    /**
624
     * Return compiled PHP code for a handlebars lookup token
625
     *
626
     * @param array<string,array|string|integer> $context current compile context
627
     * @param array<boolean|integer|string|array> $vars parsed arguments list
628
     * @param boolean $raw is this {{{ token or not
629
     * @param boolean $nosep true to compile without seperator
630
     *
631
     * @return string Return compiled code segment for the token
632
     */
633 3
    protected static function compileLookup(&$context, &$vars, $raw, $nosep = false) {
634 3
        $v2 = static::getVariableName($context, $vars[2]);
635 3
        $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...
636 3
        $sep = $nosep ? '' : $context['ops']['seperator'];
637
638 3
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
639 3
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}){$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...
640
        } else {
641
            return $raw ? "{$sep}$v[0]{$sep}" : "{$sep}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$sep}";
642
        }
643
    }
644
645
    /**
646
     * Return compiled PHP code for template output
647
     *
648
     * @param array<string,array|string|integer> $context current compile context
649
     * @param string $variable PHP code for the variable
650
     * @param string $expression normalized handlebars expression
651
     * @param boolean $raw is this {{{ token or not
652
     * @param boolean $nosep true to compile without seperator
653
     *
654
     * @return string Return compiled code segment for the token
655
     */
656 471
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
657 471
        $sep = $nosep ? '' : $context['ops']['seperator'];
658 471
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
659 425
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
660
        } else {
661 46
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
662
        }
663
    }
664
665
    /**
666
     * Return compiled PHP code for a handlebars variable token
667
     *
668
     * @param array<string,array|string|integer> $context current compile context
669
     * @param array<boolean|integer|string|array> $vars parsed arguments list
670
     * @param boolean $raw is this {{{ token or not
671
     * @param boolean $nosep true to compile without seperator
672
     *
673
     * @return string Return compiled code segment for the token
674
     */
675 367
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
676 367
        if ($context['flags']['lambda']) {
677 227
            $V = array_shift($vars);
678 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
679
        } else {
680 140
            $v = static::getVariableName($context, $vars[0]);
681
        }
682 367
        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...
683
    }
684
685
    /**
686
     * Add usage count to context
687
     *
688
     * @param array<string,array|string|integer> $context current context
689
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
690
     * @param string $name used name
691
     * @param integer $count increment
692
     *
693
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
694
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
695
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
696
     */
697 607
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
698 607
        if (!isset($context['usedCount'][$category][$name])) {
699 607
            $context['usedCount'][$category][$name] = 0;
700
        }
701 607
        return ($context['usedCount'][$category][$name] += $count);
702
    }
703
}
704
705