GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — v0.89-develop ( f64610...23b58f )
by Zordius
02:13
created

Compiler   D

Complexity

Total Complexity 131

Size/Duplication

Total Lines 598
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 89.33%

Importance

Changes 59
Bugs 6 Features 5
Metric Value
wmc 131
c 59
b 6
f 5
lcom 1
cbo 6
dl 0
loc 598
ccs 226
cts 253
cp 0.8933
rs 4.8718

20 Methods

Rating   Name   Duplication   Size   Complexity  
B compileTemplate() 0 34 5
A composePHPRender() 0 54 3
A getFuncName() 0 13 4
A getVariableNames() 0 15 4
A compileSubExpression() 0 14 4
A getVariableNameOrSubExpression() 0 3 2
F getVariableName() 0 45 25
C compileToken() 0 29 7
C partial() 0 23 8
A invertedSection() 0 4 1
B blockCustomHelper() 0 11 5
D blockEnd() 0 31 10
B blockBegin() 0 20 11
C section() 0 21 10
A with() 0 7 4
B customHelper() 0 12 7
A doElse() 0 10 3
B compileLookup() 0 9 7
B compileVariable() 0 13 9
A addUsageCount() 0 6 2

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2015 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file 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 693
    public static function compileTemplate(&$context, $template) {
44 693
        array_unshift($context['parsed'], array());
45 693
        Validator::verify($context, $template);
46
47 693
        if (count($context['error'])) {
48 68
            return;
49
        }
50
51
        // Do PHP code generation.
52 626
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 626
        Partial::handleDynamicPartial($context);
56
57 626
        $code = '';
58 626
        foreach ($context['parsed'][0] as $info) {
59 626
            if (is_array($info)) {
60 585
                $context['tokens']['current']++;
61 585
                $tmpl = static::compileToken($context, $info);
62 585
                if ($tmpl == $context['ops']['seperator']) {
63 1
                    $tmpl = '';
64
                } else {
65 584
                    $tmpl = "'$tmpl'";
66
                }
67 585
                $code .= $tmpl;
68
            } else {
69 626
                $code .= $info;
70
            }
71
        }
72
73 626
        static::$lastParsed = array_shift($context['parsed']);
74
75 626
        return $code;
76
    }
77
78
    /**
79
     * Compose LightnCandy render codes for include()
80
     *
81
     * @param array<string,array|string|integer> $context Current context
82
     * @param string $code generated PHP code
83
     *
84
     * @return string Composed PHP code
85
     */
86 625
    public static function composePHPRender($context, $code) {
87 625
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
88 625
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
89 625
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
90 625
        $flagProp = Expression::boolString($context['flags']['prop']);
91 625
        $flagMethod = Expression::boolString($context['flags']['method']);
92 625
        $flagLambda = Expression::boolString($context['flags']['lambda']);
93 625
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
94 625
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
95 625
        $flagEcho = Expression::boolString($context['flags']['echo']);
96 625
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
97 625
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
98
99 625
        $libstr = Exporter::runtime($context);
100 625
        $constants = Exporter::constants($context);
101 625
        $helpers = Exporter::helpers($context);
102 625
        $bhelpers = Exporter::helpers($context, 'blockhelpers');
103 625
        $hbhelpers = Exporter::helpers($context, 'hbhelpers');
104 625
        $debug = Runtime::DEBUG_ERROR_LOG;
105 625
        $phpstart = $context['flags']['bare'] ? '' : "<?php use {$context['runtime']} as LR;\n";
106 625
        $phpend = $context['flags']['bare'] ? ';' : "\n?>";
107
108
        // Return generated PHP code string.
109 625
        return "{$phpstart}return function (\$in, \$options = null) {
110
    \$cx = array(
111
        'flags' => array(
112
            'jstrue' => $flagJStrue,
113
            'jsobj' => $flagJSObj,
114
            'spvar' => $flagSPVar,
115
            'prop' => $flagProp,
116
            'method' => $flagMethod,
117
            'lambda' => $flagLambda,
118
            'mustlok' => $flagMustlok,
119
            'mustlam' => $flagMustlam,
120
            'echo' => $flagEcho,
121
            'partnc' => $flagPartNC,
122
            'knohlp' => $flagKnownHlp,
123
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
124
        ),
125
        'constants' => $constants,
126
        'helpers' => $helpers,
127
        'blockhelpers' => $bhelpers,
128
        'hbhelpers' => isset(\$options['helpers']) ? array_merge($hbhelpers, \$options['helpers']) : $hbhelpers,
129 625
        'partials' => array({$context['partialCode']}),
130
        'scopes' => array(),
131
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
132
        'blparam' => array(),
133 625
        'runtime' => '{$context['runtime']}',
134
$libstr
135
    );
136 625
    {$context['renderex']}
137 625
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
138 625
}$phpend";
139
    }
140
141
    /**
142
     * Get function name for standalone or none standalone template.
143
     *
144
     * @param array<string,array|string|integer> $context Current context of compiler progress.
145
     * @param string $name base function name
146
     * @param string $tag original handlabars tag for debug
147
     *
148
     * @return string compiled Function name
149
     *
150
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
151
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
152
     * @expect "\$cx['funcs']['test3'](" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime'), 'test3', ''
153
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime'), 'test', 'abc'
154
     */
155 561
    protected static function getFuncName(&$context, $name, $tag) {
156 561
        static::addUsageCount($context, 'runtime', $name);
157
158 561
        if ($context['flags']['debug'] && ($name != 'miss')) {
159 10
            $dbg = "'$tag', '$name', ";
160 10
            $name = 'debug';
161 10
            static::addUsageCount($context, 'runtime', 'debug');
162
        } else {
163 559
            $dbg = '';
164
        }
165
166 561
        return $context['flags']['standalone'] ? "\$cx['funcs']['$name']($dbg" : "LR::$name($dbg";
167
    }
168
169
    /**
170
     * Get string presentation of variables
171
     *
172
     * @param array<string,array|string|integer> $context current compile context
173
     * @param array<array> $vn variable name array.
174
     * @param array<string>|null $blockParams block param list
175
     *
176
     * @return array<string|array> variable names
177
     *
178
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
179
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
180
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
181
     */
182 238
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
183 238
        $vars = array(array(), array());
184 238
        $exps = array();
185 238
        foreach ($vn as $i => $v) {
186 199
            $V = static::getVariableNameOrSubExpression($context, $v);
187 199
            if (is_string($i)) {
188 36
                $vars[1][] = "'$i'=>{$V[0]}";
189
            } else {
190 185
                $vars[0][] = $V[0];
191
            }
192 199
            $exps[] = $V[1];
193
        }
194 238
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
195 238
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
196
    }
197
198
    /**
199
     * Get string presentation of a sub expression
200
     *
201
     * @param array<string,array|string|integer> $context current compile context
202
     * @param array<boolean|integer|string|array> $vars parsed arguments list
203
     *
204
     * @return array<string> code representing passed expression
205
     */
206 41
    public static function compileSubExpression(&$context, $vars) {
207 41
        $origSeperator = $context['ops']['seperator'];
208 41
        $context['ops']['seperator'] = '';
209
210 41
        $ret = static::customHelper($context, $vars, true);
211
212 41
        if (($ret === null) && $context['flags']['lambda']) {
213 4
            $ret = static::compileVariable($context, $vars, true);
214
        }
215
216 41
        $context['ops']['seperator'] = $origSeperator;
217
218 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
219
    }
220
221
    /**
222
     * Get string presentation of a subexpression or a variable
223
     *
224
     * @param array<array|string|integer> $context current compile context
225
     * @param array<array|string|integer> $var variable parsed path
226
     *
227
     * @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...
228
     */
229 340
    protected static function getVariableNameOrSubExpression(&$context, $var) {
230 340
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
231
    }
232
233
    /**
234
     * Get string presentation of a variable
235
     *
236
     * @param array<array|string|integer> $var variable parsed path
237
     * @param array<array|string|integer> $context current compile context
238
     * @param array<string> $lookup extra lookup string as valid PHP variable name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $lookup not be string[]|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
239
     *
240
     * @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...
241
     *
242
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
243
     * @expect array('((isset($in[\'true\']) && is_array($in)) ? $in[\'true\'] : null)', '[true]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('true')
244
     * @expect array('((isset($in[\'false\']) && is_array($in)) ? $in[\'false\'] : null)', '[false]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('false')
245
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
246
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
247
     * @expect array('((isset($in[\'2\']) && is_array($in)) ? $in[\'2\'] : null)', '[2]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('2')
248
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
249
     * @expect array('((isset($in[\'@index\']) && is_array($in)) ? $in[\'@index\'] : null)', '[@index]') when input array('flags'=>array('spvar'=>false,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@index')
250
     * @expect array("((isset(\$cx['sp_vars']['index']) && is_array(\$cx['sp_vars'])) ? \$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)), array('@index')
251
     * @expect array("((isset(\$cx['sp_vars']['key']) && is_array(\$cx['sp_vars'])) ? \$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)), array('@key')
252
     * @expect array("((isset(\$cx['sp_vars']['first']) && is_array(\$cx['sp_vars'])) ? \$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)), array('@first')
253
     * @expect array("((isset(\$cx['sp_vars']['last']) && is_array(\$cx['sp_vars'])) ? \$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)), array('@last')
254
     * @expect array('((isset($in[\'"a"\']) && is_array($in)) ? $in[\'"a"\'] : null)', '["a"]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('"a"')
255
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
256
     * @expect array('((isset($in[\'a\']) && is_array($in)) ? $in[\'a\'] : null)', '[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('a')
257
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-1])) ? $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)), array(1,'a')
258
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-3])) ? $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)), array(3,'a')
259
     * @expect array('((isset($in[\'id\']) && is_array($in)) ? $in[\'id\'] : null)', 'this.[id]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array(null, 'id')
260
     * @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,'standalone'=>0), 'runtime' => 'Runtime'), array(null, 'id')
261
     */
262 556
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
263 556
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
264 78
            if ($var[1] === "undefined") {
265 2
                $var[1] = "null";
266
            }
267 78
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
268
        }
269
270 518
        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...
271 518
        $exp = Expression::toString($levels, $spvar, $var);
272 518
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
273
274
        // change base when trace to parent
275 518
        if ($levels > 0) {
276 38
            if ($spvar) {
277 2
                $base .= str_repeat("['_parent']", $levels);
278
            } else {
279 36
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
280
            }
281
        }
282
283 518
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
284 115
            return array($base, $exp);
285
        }
286
287 476
        if ($var[0] === null) {
288 1
            array_shift($var);
289
        }
290
291
        // To support recursive context lookup, instance properties + methods and lambdas
292
        // the only way is using slower rendering time variable resolver.
293 476
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
294 378
            $L = $lookup ? ", $lookup[0]" : '';
295 378
            $A = $args ? ",$args[0]" : '';
296 378
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
297 378
            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");
298
        }
299
300 99
        $n = Expression::arrayString($var);
301 99
        array_pop($var);
302 99
        $L = $lookup ? "[{$lookup[0]}]" : '';
303 99
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
304
305 99
        return array("((isset($base$n$L) && is_array($base$p)) ? $base$n$L : " . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ')', $lookup ? "lookup $exp $lookup[1]" : $exp);
306
    }
307
308
    /**
309
     * Return compiled PHP code for a handlebars token
310
     *
311
     * @param array<string,array|string|integer> $context current compile context
312
     * @param array<string,array|boolean> $info parsed information
313
     *
314
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be string|integer|double|boolean?

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

Loading history...
315
     */
316 585
    protected static function compileToken(&$context, $info) {
317 585
        list($raw, $vars, $token, $indent) = $info;
318
319 585
        $context['tokens']['partialind'] = $indent;
320 585
        $context['currentToken'] = $token;
321
322
        // Do not touch the tag, keep it as is.
323 585
        if ($raw === -1) {
324
            return ".'" . Token::toString($token) . "'.";
325
        }
326
327 585
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
328 351
            return $ret;
329
        }
330
331 464
        if (isset($vars[0][0])) {
332 432
            if ($ret = static::customHelper($context, $vars, $raw)) {
333 112
                return $ret;
334
            }
335 327
            if ($vars[0][0] === 'else') {
336 24
                return static::doElse($context);
337
            }
338 312
            if ($vars[0][0] === 'lookup') {
339 2
                return static::compileLookup($context, $vars, $raw);
340
            }
341
        }
342
343 349
        return static::compileVariable($context, $vars, $raw);
344
    }
345
346
    /**
347
     * handle partial
348
     *
349
     * @param array<string,array|string|integer> $context current compile context
350
     * @param array<boolean|integer|string|array> $vars parsed arguments list
351
     *
352
     * @return string Return compiled code segment for the partial
353
     */
354 65
    public static function partial(&$context, $vars) {
355
        // mustache spec: ignore missing partial
356 65
        if (($context['usedFeature']['dynpartial'] === 0) && !isset($context['usedPartial'][$vars[0][0]])) {
357 1
            return $context['ops']['seperator'];
358
        }
359 64
        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...
360 64
        $p = array_shift($vars);
361 64
        if ($context['flags']['runpart']) {
362 57
            if (!isset($vars[0])) {
363 51
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
364
            }
365 57
            $v = static::getVariableNames($context, $vars);
366 57
            $tag = ">$p[0] " .implode(' ', $v[1]);
367 57
            if (Parser::isSubExp($p)) {
368 4
                list($p) = static::compileSubExpression($context, $p[1]);
369
            } else {
370 53
                $p = "'$p[0]'";
371
            }
372 57
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
373 57
            return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, $p, $v[0]$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...
374
        }
375 7
        return "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$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...
376
    }
377
378
    /**
379
     * Return compiled PHP code for a handlebars inverted section begin token
380
     *
381
     * @param array<string,array|string|integer> $context current compile context
382
     * @param array<boolean|integer|string|array> $vars parsed arguments list
383
     *
384
     * @return string Return compiled code segment for the token
385
     */
386 38
    protected static function invertedSection(&$context, $vars) {
387 38
        $v = static::getVariableName($context, $vars[0]);
388 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
389
    }
390
391
    /**
392
     * Return compiled PHP code for a handlebars block custom helper begin token
393
     *
394
     * @param array<string,array|string|integer> $context current compile context
395
     * @param array<boolean|integer|string|array> $vars parsed arguments list
396
     * @param boolean $inverted the logic will be inverted
397
     *
398
     * @return string Return compiled code segment for the token
399
     */
400 61
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
401 61
        $notHBCH = !isset($context['hbhelpers'][$vars[0][0]]);
402
403 61
        $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...
404 61
        $ch = array_shift($vars);
405 61
        $inverted = $inverted ? 'true' : 'false';
406 61
        static::addUsageCount($context, $notHBCH ? 'blockhelpers' : 'hbhelpers', $ch[0]);
407 61
        $v = static::getVariableNames($context, $vars, $bp);
408
409 61
        return $context['ops']['seperator'] . static::getFuncName($context, $notHBCH ? 'bch' : 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}";
410
    }
411
412
    /**
413
     * Return compiled PHP code for a handlebars block end token
414
     *
415
     * @param array<string,array|string|integer> $context current compile context
416
     * @param array<boolean|integer|string|array> $vars parsed arguments list
417
     *
418
     * @return string Return compiled code segment for the token
419
     */
420 309
    protected static function blockEnd(&$context, $vars) {
421 309
        $pop = $context['stack'][count($context['stack']) - 1];
422 309
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
423
            case 'if':
424
            case 'unless':
425 69
                if ($pop === ':') {
426 19
                    array_pop($context['stack']);
427 19
                    return "{$context['ops']['cnd_end']}";
428
                }
429 50
                if (!$context['flags']['nohbh']) {
430 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
431
                }
432 2
                break;
433 236
            case 'with':
434 25
                if (!$context['flags']['nohbh']) {
435 24
                    return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
436
                }
437
        }
438
439 239
        if ($pop === ':') {
440
            array_pop($context['stack']);
441
            return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
442
        }
443
444
        switch($pop) {
445 239
            case '#':
446 214
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
447
            case '^':
448 33
                return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
449
        }
450
    }
451
452
    /**
453
     * Return compiled PHP code for a handlebars block begin token
454
     *
455
     * @param array<string,array|string|integer> $context current compile context
456
     * @param array<boolean|integer|string|array> $vars parsed arguments list
457
     *
458
     * @return string Return compiled code segment for the token
459
     */
460 225
    protected static function blockBegin(&$context, $vars) {
461 225
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
462 225
        if (!$context['flags']['nohbh']) {
463 187
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
464 4
                case 'if':
465 59
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
466 59
                    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...
467 4
                case 'unless':
468 3
                    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...
469 4
                case 'each':
470 47
                    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...
471 74
                case 'with':
472 20
                    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...
473 20
                        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...
474
                    }
475
            }
476
        }
477
478 112
        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...
479
    }
480
481
    /**
482
     * compile {{#foo}} token
483
     *
484
     * @param array<string,array|string|integer> $context current compile context
485
     * @param array<boolean|integer|string|array> $vars parsed arguments list
486
     * @param boolean $isEach the section is #each
487
     *
488
     * @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...
489
     */
490 159
    protected static function section(&$context, $vars, $isEach = false) {
491 159
        $bs = 'null';
492 159
        $be = '';
493 159
        if ($isEach) {
494 47
            $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...
495 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
496 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
497 47
            array_shift($vars);
498 47
            if (!isset($vars[0])) {
499
                $vars[0] = array(null);
500
            }
501
        }
502 159
        if ($context['flags']['lambda'] && !$isEach) {
503 66
            $V = array_shift($vars);
504 66
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
505
        } else {
506 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
507
        }
508 159
        $each = $isEach ? 'true' : 'false';
509 159
        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...
510
    }
511
512
    /**
513
     * compile {{with}} token
514
     *
515
     * @param array<string,array|string|integer> $context current compile context
516
     * @param array<boolean|integer|string|array> $vars parsed arguments list
517
     *
518
     * @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...
519
     */
520 20
    protected static function with(&$context, $vars) {
521 20
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
522 20
        $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...
523 20
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
524 20
        $be = $bp ? " as |$bp[0]|" : '';
525 20
        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...
526
    }
527
528
    /**
529
     * Return compiled PHP code for a handlebars custom helper token
530
     *
531
     * @param array<string,array|string|integer> $context current compile context
532
     * @param array<boolean|integer|string|array> $vars parsed arguments list
533
     * @param boolean $raw is this {{{ token or not
534
     *
535
     * @return string|null Return compiled code segment for the token when the token is custom helper
536
     */
537 440
    protected static function customHelper(&$context, $vars, $raw) {
538 440
        $notHH = !isset($context['hbhelpers'][$vars[0][0]]);
539 440
        if (!isset($context['helpers'][$vars[0][0]]) && $notHH) {
540 330
            return;
541
        }
542
543 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
544 123
        $ch = array_shift($vars);
545 123
        $v = static::getVariableNames($context, $vars);
546 123
        static::addUsageCount($context, $notHH ? 'helpers' : 'hbhelpers', $ch[0]);
547 123
        return $context['ops']['seperator'] . static::getFuncName($context, $notHH ? 'ch' : 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn'" . ($notHH ? '' : ', $in') . "){$context['ops']['seperator']}";
548
    }
549
550
    /**
551
     * Return compiled PHP code for a handlebars else token
552
     *
553
     * @param array<string,array|string|integer> $context current compile context
554
     *
555
     * @return string Return compiled code segment for the token when the token is else
556
     */
557 46
    protected static function doElse(&$context) {
558 46
        switch ($context['stack'][count($context['stack']) - 2]) {
559
            case '[if]':
560 27
            case '[unless]':
561 19
                $context['stack'][] = ':';
562 19
                return "{$context['ops']['cnd_else']}";
563
            default:
564 27
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
565
        }
566
    }
567
568
    /**
569
     * Return compiled PHP code for a handlebars lookup token
570
     *
571
     * @param array<string,array|string|integer> $context current compile context
572
     * @param array<boolean|integer|string|array> $vars parsed arguments list
573
     * @param boolean $raw is this {{{ token or not
574
     *
575
     * @return string Return compiled code segment for the token
576
     */
577 2
    protected static function compileLookup(&$context, &$vars, $raw) {
578 2
        $v2 = static::getVariableName($context, $vars[2]);
579 2
        $v = static::getVariableName($context, $vars[1], $v2);
0 ignored issues
show
Documentation introduced by
$v2 is of type array<integer,array|stri...teger,string>|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...
580 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
581 2
            return $context['ops']['seperator'] . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}";
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...
582
        } else {
583
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
584
        }
585
    }
586
587
    /**
588
     * Return compiled PHP code for a handlebars variable token
589
     *
590
     * @param array<string,array|string|integer> $context current compile context
591
     * @param array<boolean|integer|string|array> $vars parsed arguments list
592
     * @param boolean $raw is this {{{ token or not
593
     *
594
     * @return string Return compiled code segment for the token
595
     */
596 352
    protected static function compileVariable(&$context, &$vars, $raw) {
597 352
        if ($context['flags']['lambda']) {
598 219
            $V = array_shift($vars);
599 219
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
600
        } else {
601 133
            $v = static::getVariableName($context, $vars[0]);
602
        }
603 352
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
604 317
            return $context['ops']['seperator'] . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}";
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...
605
        } else {
606 35
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
607
        }
608
    }
609
610
    /**
611
     * Add usage count to context
612
     *
613
     * @param array<string,array|string|integer> $context current context
614
     * @param string $category ctegory name, can be one of: 'var', 'helpers', 'blockhelpers'
615
     * @param string $name used name
616
     * @param integer $count increment
617
     *
618
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
619
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
620
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
621
     */
622 561
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
623 561
        if (!isset($context['usedCount'][$category][$name])) {
624 561
            $context['usedCount'][$category][$name] = 0;
625
        }
626 561
        return ($context['usedCount'][$category][$name] += $count);
627
    }
628
}
629
630