GitHub Access Token became invalid

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

Compiler::invertedSection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2016 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file of LightnCandy Compiler
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
use \LightnCandy\Validator;
24
use \LightnCandy\Token;
25
use \LightnCandy\Expression;
26
use \LightnCandy\Parser;
27
28
/**
29
 * LightnCandy Compiler
30
 */
31
class Compiler extends Validator
32
{
33
    public static $lastParsed;
34
35
    /**
36
     * Compile template into PHP code
37
     *
38
     * @param array<string,array|string|integer> $context Current context
39
     * @param string $template handlebars template
40
     *
41
     * @return string|null generated PHP code
42
     */
43 726
    public static function compileTemplate(&$context, $template) {
44 726
        array_unshift($context['parsed'], array());
45 726
        Validator::verify($context, $template);
46
47 726
        if (count($context['error'])) {
48 75
            return;
49
        }
50
51
        // Do PHP code generation.
52 652
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 652
        Partial::handleDynamic($context);
56
57 652
        $code = '';
58 652
        foreach ($context['parsed'][0] as $info) {
59 652
            if (is_array($info)) {
60 611
                $context['tokens']['current']++;
61 611
                $code .= "'" . static::compileToken($context, $info) . "'";
62
            } else {
63 652
                $code .= $info;
64
            }
65
        }
66
67 652
        static::$lastParsed = array_shift($context['parsed']);
68
69 652
        return $code;
70
    }
71
72
    /**
73
     * Compose LightnCandy render codes for include()
74
     *
75
     * @param array<string,array|string|integer> $context Current context
76
     * @param string $code generated PHP code
77
     *
78
     * @return string Composed PHP code
79
     */
80 651
    public static function composePHPRender($context, $code) {
81 651
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
82 651
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
83 651
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
84 651
        $flagProp = Expression::boolString($context['flags']['prop']);
85 651
        $flagMethod = Expression::boolString($context['flags']['method']);
86 651
        $flagLambda = Expression::boolString($context['flags']['lambda']);
87 651
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
88 651
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
89 651
        $flagEcho = Expression::boolString($context['flags']['echo']);
90 651
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
91 651
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
92
93 651
        $constants = Exporter::constants($context);
94 651
        $helpers = Exporter::helpers($context);
95 651
        $partials = implode(",\n", $context['partialCode']);
96 651
        $debug = Runtime::DEBUG_ERROR_LOG;
97 651
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as LR;";
98 651
        $safeString = ($context['usedFeature']['enc'] > 0) ? "use {$context['safestring']} as SafeString;" : '';
99
100
        // Return generated PHP code string.
101
        return <<<VAREND
102 651
$safeString{$use}return function (\$in, \$options = null) {
103
    \$helpers = $helpers;
104
    \$partials = array($partials);
105
    \$cx = array(
106
        'flags' => array(
107
            'jstrue' => $flagJStrue,
108
            'jsobj' => $flagJSObj,
109
            'spvar' => $flagSPVar,
110
            'prop' => $flagProp,
111
            'method' => $flagMethod,
112
            'lambda' => $flagLambda,
113
            'mustlok' => $flagMustlok,
114
            'mustlam' => $flagMustlam,
115
            'echo' => $flagEcho,
116
            'partnc' => $flagPartNC,
117
            'knohlp' => $flagKnownHlp,
118
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
119
        ),
120
        'constants' => $constants,
121
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
122
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
123
        'scopes' => array(),
124
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
125
        'blparam' => array(),
126 651
        'runtime' => '{$context['runtime']}',
127
    );
128 651
    {$context['renderex']}
129 651
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
130 651
};
131
VAREND
132
        ;
133
    }
134
135
    /**
136
     * Get function name for standalone or none standalone template.
137
     *
138
     * @param array<string,array|string|integer> $context Current context of compiler progress.
139
     * @param string $name base function name
140
     * @param string $tag original handlabars tag for debug
141
     *
142
     * @return string compiled Function name
143
     *
144
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
145
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
146
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'funcprefix' => 'lala_abc'), 'test3', ''
147
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'funcprefix' => 'haha456'), 'test', 'abc'
148
     */
149 586
    protected static function getFuncName(&$context, $name, $tag) {
150 586
        static::addUsageCount($context, 'runtime', $name);
151
152 586
        if ($context['flags']['debug'] && ($name != 'miss')) {
153 10
            $dbg = "'$tag', '$name', ";
154 10
            $name = 'debug';
155 10
            static::addUsageCount($context, 'runtime', 'debug');
156
        } else {
157 584
            $dbg = '';
158
        }
159
160 586
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "LR::$name($dbg";
161
    }
162
163
    /**
164
     * Get string presentation of variables
165
     *
166
     * @param array<string,array|string|integer> $context current compile context
167
     * @param array<array> $vn variable name array.
168
     * @param array<string>|null $blockParams block param list
169
     *
170
     * @return array<string|array> variable names
171
     *
172
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
173
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
174
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
175
     */
176 258
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
177 258
        $vars = array(array(), array());
178 258
        $exps = array();
179 258
        foreach ($vn as $i => $v) {
180 220
            $V = static::getVariableNameOrSubExpression($context, $v);
181 220
            if (is_string($i)) {
182 36
                $vars[1][] = "'$i'=>{$V[0]}";
183
            } else {
184 206
                $vars[0][] = $V[0];
185
            }
186 220
            $exps[] = $V[1];
187
        }
188 258
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
189 258
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
190
    }
191
192
    /**
193
     * Get string presentation of a sub expression
194
     *
195
     * @param array<string,array|string|integer> $context current compile context
196
     * @param array<boolean|integer|string|array> $vars parsed arguments list
197
     *
198
     * @return array<string> code representing passed expression
199
     */
200 41
    public static function compileSubExpression(&$context, $vars) {
201 41
        $ret = static::customHelper($context, $vars, true, true);
202
203 41
        if (($ret === null) && $context['flags']['lambda']) {
204 4
            $ret = static::compileVariable($context, $vars, true, true);
205
        }
206
207 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
208
    }
209
210
    /**
211
     * Get string presentation of a subexpression or a variable
212
     *
213
     * @param array<array|string|integer> $context current compile context
214
     * @param array<array|string|integer> $var variable parsed path
215
     *
216
     * @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...
217
     */
218 367
    protected static function getVariableNameOrSubExpression(&$context, $var) {
219 367
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
220
    }
221
222
    /**
223
     * Get string presentation of a variable
224
     *
225
     * @param array<array|string|integer> $var variable parsed path
226
     * @param array<array|string|integer> $context current compile context
227
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
228
     *
229
     * @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...
230
     *
231
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
232
     * @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')
233
     * @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')
234
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
235
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
236
     * @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')
237
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
238
     * @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')
239
     * @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')
240
     * @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')
241
     * @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')
242
     * @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')
243
     * @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"')
244
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
245
     * @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')
246
     * @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')
247
     * @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')
248
     * @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')
249
     * @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')
250
     */
251 582
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
252 582
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
253 79
            if ($var[1] === "undefined") {
254 2
                $var[1] = "null";
255
            }
256 79
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
257
        }
258
259 543
        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...
260 543
        $exp = Expression::toString($levels, $spvar, $var);
261 543
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
262
263
        // change base when trace to parent
264 543
        if ($levels > 0) {
265 39
            if ($spvar) {
266 2
                $base .= str_repeat("['_parent']", $levels);
267
            } else {
268 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
269
            }
270
        }
271
272 543
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
273 133
            return array($base, $exp);
274
        }
275
276 487
        if ($var[0] === null) {
277 1
            array_shift($var);
278
        }
279
280
        // To support recursive context lookup, instance properties + methods and lambdas
281
        // the only way is using slower rendering time variable resolver.
282 487
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
283 386
            $L = $lookup ? ", $lookup[0]" : '';
284 386
            $A = $args ? ",$args[0]" : '';
285 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
286 386
            return array(static::getFuncName($context, 'v', $exp) . "\$cx, \$in, isset($base) ? $base : null, array(" . Expression::listString($var) . "$L)$A)", $lookup ? "lookup $exp $lookup[1]" : "$exp$E");
287
        }
288
289 102
        $n = Expression::arrayString($var);
290 102
        array_pop($var);
291 102
        $L = $lookup ? "[{$lookup[0]}]" : '';
292 102
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
293
294 102
        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);
295
    }
296
297
    /**
298
     * Return compiled PHP code for a handlebars token
299
     *
300
     * @param array<string,array|string|integer> $context current compile context
301
     * @param array<string,array|boolean> $info parsed information
302
     *
303
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer|double|string?

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

Loading history...
304
     */
305 611
    protected static function compileToken(&$context, $info) {
306 611
        list($raw, $vars, $token, $indent) = $info;
307
308 611
        $context['tokens']['partialind'] = $indent;
309 611
        $context['currentToken'] = $token;
310
311 611
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
312 375
            return $ret;
313
        }
314
315 474
        if (isset($vars[0][0])) {
316 442
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
317 112
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
318
            }
319 337
            if ($vars[0][0] === 'else') {
320 26
                return static::doElse($context, $vars);
321
            }
322 322
            if ($vars[0][0] === 'lookup') {
323 2
                return static::compileLookup($context, $vars, $raw);
324
            }
325 320
            if ($vars[0][0] === 'log') {
326 2
                return static::compileLog($context, $vars, $raw);
327
            }
328
        }
329
330 355
        return static::compileVariable($context, $vars, $raw, false);
331
    }
332
333
    /**
334
     * handle partial
335
     *
336
     * @param array<string,array|string|integer> $context current compile context
337
     * @param array<boolean|integer|string|array> $vars parsed arguments list
338
     *
339
     * @return string Return compiled code segment for the partial
340
     */
341 87
    public static function partial(&$context, $vars) {
342 87
        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...
343 87
        $p = array_shift($vars);
344 87
        if ($context['flags']['runpart']) {
345 78
            if (!isset($vars[0])) {
346 71
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
347
            }
348 78
            $v = static::getVariableNames($context, $vars);
349 78
            $tag = ">$p[0] " .implode(' ', $v[1]);
350 78
            if (Parser::isSubExp($p)) {
351 4
                list($p) = static::compileSubExpression($context, $p[1]);
352
            } else {
353 74
                $p = "'$p[0]'";
354
            }
355 78
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
356 78
            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...
357
        }
358 9
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
359
    }
360
361
    /**
362
     * handle inline partial
363
     *
364
     * @param array<string,array|string|integer> $context current compile context
365
     * @param array<boolean|integer|string|array> $vars parsed arguments list
366
     *
367
     * @return string Return compiled code segment for the partial
368
     */
369 10
    public static function inline(&$context, $vars) {
370 10
        Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
371 10
        list($code) = array_shift($vars);
372 10
        $p = array_shift($vars);
373 10
        if (!isset($vars[0])) {
374 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
375
        }
376 10
        $v = static::getVariableNames($context, $vars);
377 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
378 10
        return $context['ops']['seperator'] . static::getFuncName($context, 'in', $tag) . "\$cx, '{$p[0]}', $code){$context['ops']['seperator']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...['ops']['seperator']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::inline of type boolean.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
379
    }
380
381
    /**
382
     * Return compiled PHP code for a handlebars inverted section begin token
383
     *
384
     * @param array<string,array|string|integer> $context current compile context
385
     * @param array<boolean|integer|string|array> $vars parsed arguments list
386
     *
387
     * @return string Return compiled code segment for the token
388
     */
389 38
    protected static function invertedSection(&$context, $vars) {
390 38
        $v = static::getVariableName($context, $vars[0]);
391 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
392
    }
393
394
    /**
395
     * Return compiled PHP code for a handlebars block custom helper begin token
396
     *
397
     * @param array<string,array|string|integer> $context current compile context
398
     * @param array<boolean|integer|string|array> $vars parsed arguments list
399
     * @param boolean $inverted the logic will be inverted
400
     *
401
     * @return string Return compiled code segment for the token
402
     */
403 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
404 58
        $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...
405 58
        $ch = array_shift($vars);
406 58
        $inverted = $inverted ? 'true' : 'false';
407 58
        static::addUsageCount($context, 'helpers', $ch[0]);
408 58
        $v = static::getVariableNames($context, $vars, $bp);
409
410 58
        return $context['ops']['seperator'] . static::getFuncName($context, 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}";
411
    }
412
413
    /**
414
     * Return compiled PHP code for a handlebars block end token
415
     *
416
     * @param array<string,array|string|integer> $context current compile context
417
     * @param array<boolean|integer|string|array> $vars parsed arguments list
418
     * @param string|null $matchop should also match to this operator
419
     *
420
     * @return string Return compiled code segment for the token
421
     */
422 317
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
423 317
        $pop = $context['stack'][count($context['stack']) - 1];
424 317
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
425 317
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
426
            case 'if':
427
            case 'unless':
428 70
                if ($pop === ':') {
429 19
                    array_pop($context['stack']);
430 19
                    return "$elsifend{$context['ops']['cnd_end']}";
431
                }
432 51
                if (!$context['flags']['nohbh']) {
433 49
                    return "{$context['ops']['cnd_else']}''$elsifend{$context['ops']['cnd_end']}";
434
                }
435 2
                break;
436 236
            case 'with':
437 31
                if (!$context['flags']['nohbh']) {
438 30
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
439
                }
440
        }
441
442 239
        if ($pop === ':') {
443 2
            array_pop($context['stack']);
444 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
445
        }
446
447 237
        if ($elsifend !== '') {
448 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
449
        }
450
451
        switch($pop) {
452 237
            case '#':
453 212
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
454
            case '^':
455 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
456
        }
457
    }
458
459
    /**
460
     * Return compiled PHP code for a handlebars block begin token
461
     *
462
     * @param array<string,array|string|integer> $context current compile context
463
     * @param array<boolean|integer|string|array> $vars parsed arguments list
464
     *
465
     * @return string Return compiled code segment for the token
466
     */
467 236
    protected static function blockBegin(&$context, $vars) {
468 236
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
469 236
        if (!$context['flags']['nohbh']) {
470 198
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
471 4
                case 'if':
472 65
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
473 65
                    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...
474 4
                case 'unless':
475 4
                    return "{$context['ops']['cnd_start']}(!" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]}, false)){$context['ops']['cnd_then']}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

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

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
476 4
                case 'each':
477 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...
478 77
                case 'with':
479 26
                    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...
480 26
                        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...
481
                    }
482
            }
483
        }
484
485 115
        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...
486
    }
487
488
    /**
489
     * compile {{#foo}} token
490
     *
491
     * @param array<string,array|string|integer> $context current compile context
492
     * @param array<boolean|integer|string|array> $vars parsed arguments list
493
     * @param boolean $isEach the section is #each
494
     *
495
     * @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...
496
     */
497 162
    protected static function section(&$context, $vars, $isEach = false) {
498 162
        $bs = 'null';
499 162
        $be = '';
500 162
        if ($isEach) {
501 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...
502 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
503 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
504 47
            array_shift($vars);
505
        }
506 162
        if ($context['flags']['lambda'] && !$isEach) {
507 69
            $V = array_shift($vars);
508 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
509
        } else {
510 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
511
        }
512 162
        $each = $isEach ? 'true' : 'false';
513 162
        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...
514
    }
515
516
    /**
517
     * compile {{with}} token
518
     *
519
     * @param array<string,array|string|integer> $context current compile context
520
     * @param array<boolean|integer|string|array> $vars parsed arguments list
521
     *
522
     * @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...
523
     */
524 26
    protected static function with(&$context, $vars) {
525 26
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
526 26
        $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...
527 26
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
528 26
        $be = $bp ? " as |$bp[0]|" : '';
529 26
        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...
530
    }
531
532
    /**
533
     * Return compiled PHP code for a handlebars custom helper token
534
     *
535
     * @param array<string,array|string|integer> $context current compile context
536
     * @param array<boolean|integer|string|array> $vars parsed arguments list
537
     * @param boolean $raw is this {{{ token or not
538
     * @param boolean $nosep true to compile without seperator
539
     *
540
     * @return string|null Return compiled code segment for the token when the token is custom helper
541
     */
542 450
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
543 450
        if (!isset($context['helpers'][$vars[0][0]])) {
544 340
            return;
545
        }
546
547 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
548 123
        $ch = array_shift($vars);
549 123
        $v = static::getVariableNames($context, $vars);
550 123
        static::addUsageCount($context, 'helpers', $ch[0]);
551 123
        $sep = $nosep ? '' : $context['ops']['seperator'];
552
553 123
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
554
    }
555
556
    /**
557
     * Return compiled PHP code for a handlebars else token
558
     *
559
     * @param array<string,array|string|integer> $context current compile context
560
     * @param array<boolean|integer|string|array> $vars parsed arguments list
561
     *
562
     * @return string Return compiled code segment for the token when the token is else
563
     */
564 48
    protected static function doElse(&$context, $vars) {
565 48
        switch ($context['stack'][count($context['stack']) - 2]) {
566
            case '[if]':
567 28
            case '[unless]':
568 23
                $context['stack'][] = ':';
569 23
                return "{$context['ops']['cnd_else']}";
570
            default:
571 28
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
572
        }
573
    }
574
575
    /**
576
     * Return compiled PHP code for a handlebars log token
577
     *
578
     * @param array<string,array|string|integer> $context current compile context
579
     * @param array<boolean|integer|string|array> $vars parsed arguments list
580
     * @param boolean $raw is this {{{ token or not
581
     *
582
     * @return string Return compiled code segment for the token
583
     */
584 2
    protected static function compileLog(&$context, &$vars, $raw) {
0 ignored issues
show
Unused Code introduced by
The parameter $raw is not used and could be removed.

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
588
    }
589
590
    /**
591
     * Return compiled PHP code for a handlebars lookup token
592
     *
593
     * @param array<string,array|string|integer> $context current compile context
594
     * @param array<boolean|integer|string|array> $vars parsed arguments list
595
     * @param boolean $raw is this {{{ token or not
596
     *
597
     * @return string Return compiled code segment for the token
598
     */
599 2
    protected static function compileLookup(&$context, &$vars, $raw) {
600 2
        $v2 = static::getVariableName($context, $vars[2]);
601 2
        $v = static::getVariableName($context, $vars[1], $v2);
0 ignored issues
show
Documentation introduced by
$v2 is of type array<integer,array|stri...rray<integer,string>"}>, but the function expects a array<integer,string>|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
602 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
603 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...
604
        } else {
605
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
606
        }
607
    }
608
609
    /**
610
     * Return compiled PHP code for template output
611
     *
612
     * @param array<string,array|string|integer> $context current compile context
613
     * @param string $variable PHP code for the variable
614
     * @param string $expression normalized handlebars expression
615
     * @param boolean $raw is this {{{ token or not
616
     * @param boolean $nosep true to compile without seperator
617
     *
618
     * @return string Return compiled code segment for the token
619
     */
620 458
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
621 458
        $sep = $nosep ? '' : $context['ops']['seperator'];
622 458
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
623 416
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
624
        } else {
625 42
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
626
        }
627
    }
628
629
    /**
630
     * Return compiled PHP code for a handlebars variable token
631
     *
632
     * @param array<string,array|string|integer> $context current compile context
633
     * @param array<boolean|integer|string|array> $vars parsed arguments list
634
     * @param boolean $raw is this {{{ token or not
635
     * @param boolean $nosep true to compile without seperator
636
     *
637
     * @return string Return compiled code segment for the token
638
     */
639 358
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
640 358
        if ($context['flags']['lambda']) {
641 227
            $V = array_shift($vars);
642 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
643
        } else {
644 131
            $v = static::getVariableName($context, $vars[0]);
645
        }
646 358
        return static::compileOutput($context, $v[0], $v[1], $raw, $nosep);
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array<integer,string>; however, LightnCandy\Compiler::compileOutput() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
647
    }
648
649
    /**
650
     * Add usage count to context
651
     *
652
     * @param array<string,array|string|integer> $context current context
653
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
654
     * @param string $name used name
655
     * @param integer $count increment
656
     *
657
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
658
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
659
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
660
     */
661 586
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
662 586
        if (!isset($context['usedCount'][$category][$name])) {
663 586
            $context['usedCount'][$category][$name] = 0;
664
        }
665 586
        return ($context['usedCount'][$category][$name] += $count);
666
    }
667
}
668
669