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 ( dbf016...e6ca53 )
by Zordius
03:50
created

Compiler   D

Complexity

Total Complexity 128

Size/Duplication

Total Lines 591
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 89.52%

Importance

Changes 56
Bugs 5 Features 5
Metric Value
wmc 128
c 56
b 5
f 5
lcom 1
cbo 6
dl 0
loc 591
ccs 222
cts 248
cp 0.8952
rs 4.8718

20 Methods

Rating   Name   Duplication   Size   Complexity  
A composePHPRender() 0 52 3
A getFuncName() 0 13 4
A getVariableNames() 0 15 4
A getVariableNameOrSubExpression() 0 3 2
B compileTemplate() 0 34 5
A compileSubExpression() 0 14 4
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
B section() 0 16 7
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 674
    public static function compileTemplate(&$context, $template) {
44 674
        array_unshift($context['parsed'], array());
45 674
        Validator::verify($context, $template);
46
47 674
        if (count($context['error'])) {
48 69
            return;
49
        }
50
51
        // Do PHP code generation.
52 606
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 606
        Partial::handleDynamicPartial($context);
56
57 606
        $code = '';
58 606
        foreach ($context['parsed'][0] as $info) {
59 606
            if (is_array($info)) {
60 565
                $context['tokens']['current']++;
61 565
                $tmpl = static::compileToken($context, $info);
62 565
                if ($tmpl == $context['ops']['seperator']) {
63 1
                    $tmpl = '';
64
                } else {
65 564
                    $tmpl = "'$tmpl'";
66
                }
67 565
                $code .= $tmpl;
68
            } else {
69 606
                $code .= $info;
70
            }
71
        }
72
73 606
        static::$lastParsed = array_shift($context['parsed']);
74
75 606
        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 605
    public static function composePHPRender($context, $code) {
87 605
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
88 605
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
89 605
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
90 605
        $flagProp = Expression::boolString($context['flags']['prop']);
91 605
        $flagMethod = Expression::boolString($context['flags']['method']);
92 605
        $flagLambda = Expression::boolString($context['flags']['lambda']);
93 605
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
94 605
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
95 605
        $flagEcho = Expression::boolString($context['flags']['echo']);
96 605
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
97
98 605
        $libstr = Exporter::runtime($context);
99 605
        $constants = Exporter::constants($context);
100 605
        $helpers = Exporter::helpers($context);
101 605
        $bhelpers = Exporter::helpers($context, 'blockhelpers');
102 605
        $hbhelpers = Exporter::helpers($context, 'hbhelpers');
103 605
        $debug = Runtime::DEBUG_ERROR_LOG;
104 605
        $phpstart = $context['flags']['bare'] ? '' : "<?php use {$context['runtime']} as LR;\n";
105 605
        $phpend = $context['flags']['bare'] ? ';' : "\n?>";
106
107
        // Return generated PHP code string.
108 605
        return "{$phpstart}return function (\$in, \$options = null) {
109
    \$cx = array(
110
        'flags' => array(
111
            'jstrue' => $flagJStrue,
112
            'jsobj' => $flagJSObj,
113
            'spvar' => $flagSPVar,
114
            'prop' => $flagProp,
115
            'method' => $flagMethod,
116
            'lambda' => $flagLambda,
117
            'mustlok' => $flagMustlok,
118
            'mustlam' => $flagMustlam,
119
            'echo' => $flagEcho,
120
            'partnc' => $flagPartNC,
121
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
122
        ),
123
        'constants' => $constants,
124
        'helpers' => $helpers,
125
        'blockhelpers' => $bhelpers,
126
        'hbhelpers' => isset(\$options['helpers']) ? array_merge($hbhelpers, \$options['helpers']) : $hbhelpers,
127 605
        'partials' => array({$context['partialCode']}),
128
        'scopes' => array(),
129
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
130
        'blparam' => array(),
131 605
        'runtime' => '{$context['runtime']}',
132
$libstr
133
    );
134 605
    {$context['renderex']}
135 605
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
136 605
}$phpend";
137
    }
138
139
    /**
140
     * Get function name for standalone or none standalone template.
141
     *
142
     * @param array<string,array|string|integer> $context Current context of compiler progress.
143
     * @param string $name base function name
144
     * @param string $tag original handlabars tag for debug
145
     *
146
     * @return string compiled Function name
147
     *
148
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
149
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
150
     * @expect "\$cx['funcs']['test3'](" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime'), 'test3', ''
151
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime'), 'test', 'abc'
152
     */
153 541
    protected static function getFuncName(&$context, $name, $tag) {
154 541
        static::addUsageCount($context, 'runtime', $name);
155
156 541
        if ($context['flags']['debug'] && ($name != 'miss')) {
157 10
            $dbg = "'$tag', '$name', ";
158 10
            $name = 'debug';
159 10
            static::addUsageCount($context, 'runtime', 'debug');
160
        } else {
161 539
            $dbg = '';
162
        }
163
164 541
        return $context['flags']['standalone'] ? "\$cx['funcs']['$name']($dbg" : "LR::$name($dbg";
165
    }
166
167
    /**
168
     * Get string presentation of variables
169
     *
170
     * @param array<string,array|string|integer> $context current compile context
171
     * @param array<array> $vn variable name array.
172
     * @param array<string>|null $blockParams block param list
173
     *
174
     * @return array<string|array> variable names
175
     *
176
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
177
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
178
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
179
     */
180 226
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
181 226
        $vars = array(array(), array());
182 226
        $exps = array();
183 226
        foreach ($vn as $i => $v) {
184 188
            $V = static::getVariableNameOrSubExpression($context, $v);
185 188
            if (is_string($i)) {
186 35
                $vars[1][] = "'$i'=>{$V[0]}";
187
            } else {
188 175
                $vars[0][] = $V[0];
189
            }
190 188
            $exps[] = $V[1];
191
        }
192 226
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
193 226
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
194
    }
195
196
    /**
197
     * Get string presentation of a sub expression
198
     *
199
     * @param array<string,array|string|integer> $context current compile context
200
     * @param array<boolean|integer|string|array> $vars parsed arguments list
201
     *
202
     * @return array<string> code representing passed expression
203
     */
204 39
    public static function compileSubExpression(&$context, $vars) {
205 39
        $origSeperator = $context['ops']['seperator'];
206 39
        $context['ops']['seperator'] = '';
207
208 39
        $ret = static::customHelper($context, $vars, true);
209
210 39
        if (($ret === null) && $context['flags']['lambda']) {
211 2
            $ret = static::compileVariable($context, $vars, true);
212
        }
213
214 39
        $context['ops']['seperator'] = $origSeperator;
215
216 39
        return array($ret ? $ret : '', 'FIXME: $subExpression');
217
    }
218
219
    /**
220
     * Get string presentation of a subexpression or a variable
221
     *
222
     * @param array<array|string|integer> $context current compile context
223
     * @param array<array|string|integer> $var variable parsed path
224
     *
225
     * @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...
226
     */
227 370
    protected static function getVariableNameOrSubExpression(&$context, $var) {
228 370
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
229
    }
230
231
    /**
232
     * Get string presentation of a variable
233
     *
234
     * @param array<array|string|integer> $var variable parsed path
235
     * @param array<array|string|integer> $context current compile context
236
     * @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...
237
     *
238
     * @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...
239
     *
240
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
241
     * @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')
242
     * @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')
243
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(0, 'true')
244
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(0, 'false')
245
     * @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')
246
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(0, '2')
247
     * @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')
248
     * @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')
249
     * @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')
250
     * @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')
251
     * @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')
252
     * @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"')
253
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(0, '"a"')
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('((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')
256
     * @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')
257
     * @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')
258
     * @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')
259
     */
260 537
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
261 537
        if (isset($var[0]) && ($var[0] === 0)) {
262 78
            if ($var[1] === "undefined") {
263 2
                $var[1] = "null";
264
            }
265 78
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
266
        }
267
268 499
        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...
269 499
        $exp = Expression::toString($levels, $spvar, $var);
270 499
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
271
272
        // change base when trace to parent
273 499
        if ($levels > 0) {
274 38
            if ($spvar) {
275 2
                $base .= str_repeat("['_parent']", $levels);
276
            } else {
277 36
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
278
            }
279
        }
280
281 499
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
282 114
            return array($base, $exp);
283
        }
284
285 457
        if ($var[0] === null) {
286 1
            array_shift($var);
287
        }
288
289
        // To support recursive context lookup, instance properties + methods and lambdas
290
        // the only way is using slower rendering time variable resolver.
291 457
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
292 359
            $L = $lookup ? ", $lookup[0]" : '';
293 359
            $A = $args ? ",$args[0]" : '';
294 359
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
295 359
            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");
296
        }
297
298 99
        $n = Expression::arrayString($var);
299 99
        array_pop($var);
300 99
        $L = $lookup ? "[{$lookup[0]}]" : '';
301 99
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
302
303 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);
304
    }
305
306
    /**
307
     * Return compiled PHP code for a handlebars token
308
     *
309
     * @param array<string,array|string|integer> $context current compile context
310
     * @param array<string,array|boolean> $info parsed information
311
     *
312
     * @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...
313
     */
314 565
    protected static function compileToken(&$context, $info) {
315 565
        list($raw, $vars, $token, $indent) = $info;
316
317 565
        $context['tokens']['partialind'] = $indent;
318 565
        $context['currentToken'] = $token;
319
320
        // Do not touch the tag, keep it as is.
321 565
        if ($raw === -1) {
322
            return ".'" . Token::toString($token) . "'.";
323
        }
324
325 565
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
326 344
            return $ret;
327
        }
328
329 448
        if (isset($vars[0][0])) {
330 416
            if ($ret = static::customHelper($context, $vars, $raw)) {
331 107
                return $ret;
332
            }
333 314
            if ($vars[0][0] === 'else') {
334 24
                return static::doElse($context);
335
            }
336 299
            if ($vars[0][0] === 'lookup') {
337 2
                return static::compileLookup($context, $vars, $raw);
338
            }
339
        }
340
341 336
        return static::compileVariable($context, $vars, $raw);
342
    }
343
344
    /**
345
     * handle partial
346
     *
347
     * @param array<string,array|string|integer> $context current compile context
348
     * @param array<boolean|integer|string|array> $vars parsed arguments list
349
     *
350
     * @return string Return compiled code segment for the partial
351
     */
352 64
    public static function partial(&$context, $vars) {
353
        // mustache spec: ignore missing partial
354 64
        if (($context['usedFeature']['dynpartial'] === 0) && !isset($context['usedPartial'][$vars[0][0]])) {
355 1
            return $context['ops']['seperator'];
356
        }
357 63
        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...
358 63
        $p = array_shift($vars);
359 63
        if ($context['flags']['runpart']) {
360 56
            if (!isset($vars[0])) {
361 50
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
362
            }
363 56
            $v = static::getVariableNames($context, $vars);
364 56
            $tag = ">$p[0] " .implode(' ', $v[1]);
365 56
            if (Parser::isSubExp($p)) {
366 4
                list($p) = static::compileSubExpression($context, $p[1]);
367
            } else {
368 52
                $p = "'$p[0]'";
369
            }
370 56
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
371 56
            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...
372
        }
373 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...
374
    }
375
376
    /**
377
     * Return compiled PHP code for a handlebars inverted section begin token
378
     *
379
     * @param array<string,array|string|integer> $context current compile context
380
     * @param array<boolean|integer|string|array> $vars parsed arguments list
381
     *
382
     * @return string Return compiled code segment for the token
383
     */
384 37
    protected static function invertedSection(&$context, $vars) {
385 37
        $v = static::getVariableName($context, $vars[0]);
386 37
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
387
    }
388
389
    /**
390
     * Return compiled PHP code for a handlebars block custom helper begin token
391
     *
392
     * @param array<string,array|string|integer> $context current compile context
393
     * @param array<boolean|integer|string|array> $vars parsed arguments list
394
     * @param boolean $inverted the logic will be inverted
395
     *
396
     * @return string Return compiled code segment for the token
397
     */
398 60
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
399 60
        $notHBCH = !isset($context['hbhelpers'][$vars[0][0]]);
400
401 60
        $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...
402 60
        $ch = array_shift($vars);
403 60
        $inverted = $inverted ? 'true' : 'false';
404 60
        static::addUsageCount($context, $notHBCH ? 'blockhelpers' : 'hbhelpers', $ch[0]);
405 60
        $v = static::getVariableNames($context, $vars, $bp);
406
407 60
        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']}";
408
    }
409
410
    /**
411
     * Return compiled PHP code for a handlebars block end token
412
     *
413
     * @param array<string,array|string|integer> $context current compile context
414
     * @param array<boolean|integer|string|array> $vars parsed arguments list
415
     *
416
     * @return string Return compiled code segment for the token
417
     */
418 303
    protected static function blockEnd(&$context, $vars) {
419 303
        $pop = $context['stack'][count($context['stack']) - 1];
420 303
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
421
            case 'if':
422
            case 'unless':
423 68
                if ($pop === ':') {
424 19
                    array_pop($context['stack']);
425 19
                    return "{$context['ops']['cnd_end']}";
426
                }
427 49
                if (!$context['flags']['nohbh']) {
428 47
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
429
                }
430 2
                break;
431 231
            case 'with':
432 25
                if (!$context['flags']['nohbh']) {
433 24
                    return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
434
                }
435
        }
436
437 234
        if ($pop === ':') {
438
            array_pop($context['stack']);
439
            return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
440
        }
441
442
        switch($pop) {
443 234
            case '#':
444 210
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
445
            case '^':
446 32
                return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
447
        }
448
    }
449
450
    /**
451
     * Return compiled PHP code for a handlebars block begin token
452
     *
453
     * @param array<string,array|string|integer> $context current compile context
454
     * @param array<boolean|integer|string|array> $vars parsed arguments list
455
     *
456
     * @return string Return compiled code segment for the token
457
     */
458 221
    protected static function blockBegin(&$context, $vars) {
459 221
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
460 221
        if (!$context['flags']['nohbh']) {
461 183
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
462 4
                case 'if':
463 59
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
464 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 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...
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...
465 4
                case 'unless':
466 2
                    return "{$context['ops']['cnd_start']}(!" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]}, false)){$context['ops']['cnd_then']}";
0 ignored issues
show
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...
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...
467 4
                case 'each':
468 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...
469 71
                case 'with':
470 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...
471 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...
472
                    }
473
            }
474
        }
475
476 109
        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...
477
    }
478
479
    /**
480
     * compile {{#foo}} token
481
     *
482
     * @param array<string,array|string|integer> $context current compile context
483
     * @param array<boolean|integer|string|array> $vars parsed arguments list
484
     * @param boolean $isEach the section is #each
485
     *
486
     * @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...
487
     */
488 156
    protected static function section(&$context, $vars, $isEach = false) {
489 156
        $bs = 'null';
490 156
        $be = '';
491 156
        if ($isEach) {
492 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...
493 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
494 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
495 47
            array_shift($vars);
496 47
            if (!isset($vars[0])) {
497
                $vars[0] = array(null);
498
            }
499
        }
500 156
        $v = static::getVariableNameOrSubExpression($context, $vars[0]);
501 156
        $each = $isEach ? 'true' : 'false';
502 156
        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...
503
    }
504
505
    /**
506
     * compile {{with}} token
507
     *
508
     * @param array<string,array|string|integer> $context current compile context
509
     * @param array<boolean|integer|string|array> $vars parsed arguments list
510
     *
511
     * @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...
512
     */
513 20
    protected static function with(&$context, $vars) {
514 20
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
515 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...
516 20
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
517 20
        $be = $bp ? " as |$bp[0]|" : '';
518 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...
519
    }
520
521
    /**
522
     * Return compiled PHP code for a handlebars custom helper token
523
     *
524
     * @param array<string,array|string|integer> $context current compile context
525
     * @param array<boolean|integer|string|array> $vars parsed arguments list
526
     * @param boolean $raw is this {{{ token or not
527
     *
528
     * @return string|null Return compiled code segment for the token when the token is custom helper
529
     */
530 424
    protected static function customHelper(&$context, $vars, $raw) {
531 424
        $notHH = !isset($context['hbhelpers'][$vars[0][0]]);
532 424
        if (!isset($context['helpers'][$vars[0][0]]) && $notHH) {
533 316
            return;
534
        }
535
536 118
        $fn = $raw ? 'raw' : $context['ops']['enc'];
537 118
        $ch = array_shift($vars);
538 118
        $v = static::getVariableNames($context, $vars);
539 118
        static::addUsageCount($context, $notHH ? 'helpers' : 'hbhelpers', $ch[0]);
540 118
        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']}";
541
    }
542
543
    /**
544
     * Return compiled PHP code for a handlebars else token
545
     *
546
     * @param array<string,array|string|integer> $context current compile context
547
     *
548
     * @return string Return compiled code segment for the token when the token is else
549
     */
550 46
    protected static function doElse(&$context) {
551 46
        switch ($context['stack'][count($context['stack']) - 2]) {
552
            case '[if]':
553 27
            case '[unless]':
554 19
                $context['stack'][] = ':';
555 19
                return "{$context['ops']['cnd_else']}";
556
            default:
557 27
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
558
        }
559
    }
560
561
    /**
562
     * Return compiled PHP code for a handlebars lookup token
563
     *
564
     * @param array<string,array|string|integer> $context current compile context
565
     * @param array<boolean|integer|string|array> $vars parsed arguments list
566
     * @param boolean $raw is this {{{ token or not
567
     *
568
     * @return string Return compiled code segment for the token
569
     */
570 2
    protected static function compileLookup(&$context, &$vars, $raw) {
571 2
        $v2 = static::getVariableName($context, $vars[2]);
572 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...
573 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
574 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...
575
        } else {
576
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
577
        }
578
    }
579
580
    /**
581
     * Return compiled PHP code for a handlebars variable token
582
     *
583
     * @param array<string,array|string|integer> $context current compile context
584
     * @param array<boolean|integer|string|array> $vars parsed arguments list
585
     * @param boolean $raw is this {{{ token or not
586
     *
587
     * @return string Return compiled code segment for the token
588
     */
589 338
    protected static function compileVariable(&$context, &$vars, $raw) {
590 338
        if ($context['flags']['lambda']) {
591 205
            $V = array_shift($vars);
592 205
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : null);
593
        } else {
594 133
            $v = static::getVariableName($context, $vars[0]);
595
        }
596 338
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
597 303
            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...
598
        } else {
599 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']}";
600
        }
601
    }
602
603
    /**
604
     * Add usage count to context
605
     *
606
     * @param array<string,array|string|integer> $context current context
607
     * @param string $category ctegory name, can be one of: 'var', 'helpers', 'blockhelpers'
608
     * @param string $name used name
609
     * @param integer $count increment
610
     *
611
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
612
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
613
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
614
     */
615 541
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
616 541
        if (!isset($context['usedCount'][$category][$name])) {
617 541
            $context['usedCount'][$category][$name] = 0;
618
        }
619 541
        return ($context['usedCount'][$category][$name] += $count);
620
    }
621
}
622
623