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 ( 898ed8...ccf3f3 )
by Zordius
02:20
created

Compiler::compileVariable()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6667
cc 3
eloc 7
nc 2
nop 4
crap 3
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 717
    public static function compileTemplate(&$context, $template) {
44 717
        array_unshift($context['parsed'], array());
45 717
        Validator::verify($context, $template);
46
47 717
        if (count($context['error'])) {
48 70
            return;
49
        }
50
51
        // Do PHP code generation.
52 648
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 648
        Partial::handleDynamic($context);
56
57 648
        $code = '';
58 648
        foreach ($context['parsed'][0] as $info) {
59 648
            if (is_array($info)) {
60 607
                $context['tokens']['current']++;
61 607
                $tmpl = static::compileToken($context, $info);
62 607
                if ($tmpl == $context['ops']['seperator']) {
63 1
                    $tmpl = '';
64
                } else {
65 606
                    $tmpl = "'$tmpl'";
66
                }
67 607
                $code .= $tmpl;
68
            } else {
69 648
                $code .= $info;
70
            }
71
        }
72
73 648
        static::$lastParsed = array_shift($context['parsed']);
74
75 648
        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 647
    public static function composePHPRender($context, $code) {
87 647
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
88 647
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
89 647
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
90 647
        $flagProp = Expression::boolString($context['flags']['prop']);
91 647
        $flagMethod = Expression::boolString($context['flags']['method']);
92 647
        $flagLambda = Expression::boolString($context['flags']['lambda']);
93 647
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
94 647
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
95 647
        $flagEcho = Expression::boolString($context['flags']['echo']);
96 647
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
97 647
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
98
99 647
        $constants = Exporter::constants($context);
100 647
        $helpers = Exporter::helpers($context);
101 647
        $partials = implode(",\n", $context['partialCode']);
102 647
        $debug = Runtime::DEBUG_ERROR_LOG;
103 647
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as LR;";
104
105
        // Return generated PHP code string.
106 647
        return "use {$context['safestring']} as SafeString;{$use}return function (\$in, \$options = null) {
107
    \$cx = array(
108
        'flags' => array(
109
            'jstrue' => $flagJStrue,
110
            'jsobj' => $flagJSObj,
111
            'spvar' => $flagSPVar,
112
            'prop' => $flagProp,
113
            'method' => $flagMethod,
114
            'lambda' => $flagLambda,
115
            'mustlok' => $flagMustlok,
116
            'mustlam' => $flagMustlam,
117
            'echo' => $flagEcho,
118
            'partnc' => $flagPartNC,
119
            'knohlp' => $flagKnownHlp,
120
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
121
        ),
122
        'constants' => $constants,
123
        'helpers' => isset(\$options['helpers']) ? array_merge($helpers, \$options['helpers']) : $helpers,
124
        'partials' => array($partials),
125
        'scopes' => array(),
126
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
127
        'blparam' => array(),
128 647
        'runtime' => '{$context['runtime']}',
129
    );
130 647
    {$context['renderex']}
131 647
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
132 647
};";
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 583
    protected static function getFuncName(&$context, $name, $tag) {
150 583
        static::addUsageCount($context, 'runtime', $name);
151
152 583
        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 581
            $dbg = '';
158
        }
159
160 583
        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 256
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
177 256
        $vars = array(array(), array());
178 256
        $exps = array();
179 256
        foreach ($vn as $i => $v) {
180 218
            $V = static::getVariableNameOrSubExpression($context, $v);
181 218
            if (is_string($i)) {
182 36
                $vars[1][] = "'$i'=>{$V[0]}";
183
            } else {
184 204
                $vars[0][] = $V[0];
185
            }
186 218
            $exps[] = $V[1];
187
        }
188 256
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
189 256
        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 364
    protected static function getVariableNameOrSubExpression(&$context, $var) {
219 364
        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 579
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
252 579
        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 540
        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 540
        $exp = Expression::toString($levels, $spvar, $var);
261 540
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
262
263
        // change base when trace to parent
264 540
        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 540
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
273 132
            return array($base, $exp);
274
        }
275
276 485
        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 485
        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 100
        $n = Expression::arrayString($var);
290 100
        array_pop($var);
291 100
        $L = $lookup ? "[{$lookup[0]}]" : '';
292 100
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
293
294 100
        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 string|boolean|integer|double?

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 607
    protected static function compileToken(&$context, $info) {
306 607
        list($raw, $vars, $token, $indent) = $info;
307
308 607
        $context['tokens']['partialind'] = $indent;
309 607
        $context['currentToken'] = $token;
310
311
        // Do not touch the tag, keep it as is.
312 607
        if ($raw === -1) {
313
            return ".'" . Token::toString($token) . "'.";
314
        }
315
316 607
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
317 372
            return $ret;
318
        }
319
320 472
        if (isset($vars[0][0])) {
321 440
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
322 112
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
323
            }
324 335
            if ($vars[0][0] === 'else') {
325 25
                return static::doElse($context, $vars);
326
            }
327 322
            if ($vars[0][0] === 'lookup') {
328 2
                return static::compileLookup($context, $vars, $raw);
329
            }
330 320
            if ($vars[0][0] === 'log') {
331 1
                return static::compileLog($context, $vars, $raw);
332
            }
333
        }
334
335 356
        return static::compileVariable($context, $vars, $raw, false);
336
    }
337
338
    /**
339
     * handle partial
340
     *
341
     * @param array<string,array|string|integer> $context current compile context
342
     * @param array<boolean|integer|string|array> $vars parsed arguments list
343
     *
344
     * @return string Return compiled code segment for the partial
345
     */
346 86
    public static function partial(&$context, $vars) {
347
        // mustache spec: ignore missing partial
348 86
        if (($context['usedFeature']['dynpartial'] === 0) && ($context['usedFeature']['inlpartial'] === 0) && !isset($context['usedPartial'][$vars[0][0]])) {
349 1
            return $context['ops']['seperator'];
350
        }
351 85
        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...
352 85
        $p = array_shift($vars);
353 85
        if ($context['flags']['runpart']) {
354 77
            if (!isset($vars[0])) {
355 70
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
356
            }
357 77
            $v = static::getVariableNames($context, $vars);
358 77
            $tag = ">$p[0] " .implode(' ', $v[1]);
359 77
            if (Parser::isSubExp($p)) {
360 4
                list($p) = static::compileSubExpression($context, $p[1]);
361
            } else {
362 73
                $p = "'$p[0]'";
363
            }
364 77
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
365 77
            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...
366
        }
367 8
        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...
368
    }
369
370
    /**
371
     * handle inline partial
372
     *
373
     * @param array<string,array|string|integer> $context current compile context
374
     * @param array<boolean|integer|string|array> $vars parsed arguments list
375
     *
376
     * @return string Return compiled code segment for the partial
377
     */
378 10
    public static function inline(&$context, $vars) {
379 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...
380 10
        list($code) = array_shift($vars);
381 10
        $p = array_shift($vars);
382 10
        if (!isset($vars[0])) {
383 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
384
        }
385 10
        $v = static::getVariableNames($context, $vars);
386 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
387 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...
388
    }
389
390
    /**
391
     * Return compiled PHP code for a handlebars inverted section begin token
392
     *
393
     * @param array<string,array|string|integer> $context current compile context
394
     * @param array<boolean|integer|string|array> $vars parsed arguments list
395
     *
396
     * @return string Return compiled code segment for the token
397
     */
398 38
    protected static function invertedSection(&$context, $vars) {
399 38
        $v = static::getVariableName($context, $vars[0]);
400 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
401
    }
402
403
    /**
404
     * Return compiled PHP code for a handlebars block custom helper begin token
405
     *
406
     * @param array<string,array|string|integer> $context current compile context
407
     * @param array<boolean|integer|string|array> $vars parsed arguments list
408
     * @param boolean $inverted the logic will be inverted
409
     *
410
     * @return string Return compiled code segment for the token
411
     */
412 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
413 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...
414 58
        $ch = array_shift($vars);
415 58
        $inverted = $inverted ? 'true' : 'false';
416 58
        static::addUsageCount($context, 'helpers', $ch[0]);
417 58
        $v = static::getVariableNames($context, $vars, $bp);
418
419 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']}";
420
    }
421
422
    /**
423
     * Return compiled PHP code for a handlebars block end token
424
     *
425
     * @param array<string,array|string|integer> $context current compile context
426
     * @param array<boolean|integer|string|array> $vars parsed arguments list
427
     * @param string|null $matchop should also match to this operator
428
     *
429
     * @return string Return compiled code segment for the token
430
     */
431 316
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
432 316
        $pop = $context['stack'][count($context['stack']) - 1];
433 316
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
434 316
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
435
            case 'if':
436
            case 'unless':
437 69
                if ($pop === ':') {
438 19
                    array_pop($context['stack']);
439 19
                    return "$elsifend{$context['ops']['cnd_end']}";
440
                }
441 50
                if (!$context['flags']['nohbh']) {
442 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
443
                }
444 2
                break;
445 236
            case 'with':
446 32
                if (!$context['flags']['nohbh']) {
447 31
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
448
                }
449
        }
450
451 239
        if ($pop === ':') {
452 2
            array_pop($context['stack']);
453 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
454
        }
455
456 237
        if ($elsifend !== '') {
457 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
458
        }
459
460
        switch($pop) {
461 237
            case '#':
462 212
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
463
            case '^':
464 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
465
        }
466
    }
467
468
    /**
469
     * Return compiled PHP code for a handlebars block begin token
470
     *
471
     * @param array<string,array|string|integer> $context current compile context
472
     * @param array<boolean|integer|string|array> $vars parsed arguments list
473
     *
474
     * @return string Return compiled code segment for the token
475
     */
476 235
    protected static function blockBegin(&$context, $vars) {
477 235
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
478 235
        if (!$context['flags']['nohbh']) {
479 197
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
480 4
                case 'if':
481 64
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
482 64
                    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...
483 4
                case 'unless':
484 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...
485 4
                case 'each':
486 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...
487 77
                case 'with':
488 27
                    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...
489 27
                        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...
490
                    }
491
            }
492
        }
493
494 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...
495
    }
496
497
    /**
498
     * compile {{#foo}} token
499
     *
500
     * @param array<string,array|string|integer> $context current compile context
501
     * @param array<boolean|integer|string|array> $vars parsed arguments list
502
     * @param boolean $isEach the section is #each
503
     *
504
     * @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...
505
     */
506 162
    protected static function section(&$context, $vars, $isEach = false) {
507 162
        $bs = 'null';
508 162
        $be = '';
509 162
        if ($isEach) {
510 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...
511 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
512 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
513 47
            array_shift($vars);
514 47
            if (!isset($vars[0])) {
515
                $vars[0] = array(null);
516
            }
517
        }
518 162
        if ($context['flags']['lambda'] && !$isEach) {
519 69
            $V = array_shift($vars);
520 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
521
        } else {
522 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
523
        }
524 162
        $each = $isEach ? 'true' : 'false';
525 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...
526
    }
527
528
    /**
529
     * compile {{with}} token
530
     *
531
     * @param array<string,array|string|integer> $context current compile context
532
     * @param array<boolean|integer|string|array> $vars parsed arguments list
533
     *
534
     * @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...
535
     */
536 27
    protected static function with(&$context, $vars) {
537 27
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
538 27
        $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...
539 27
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
540 27
        $be = $bp ? " as |$bp[0]|" : '';
541 27
        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...
542
    }
543
544
    /**
545
     * Return compiled PHP code for a handlebars custom helper token
546
     *
547
     * @param array<string,array|string|integer> $context current compile context
548
     * @param array<boolean|integer|string|array> $vars parsed arguments list
549
     * @param boolean $raw is this {{{ token or not
550
     * @param boolean $nosep true to compile without seperator
551
     *
552
     * @return string|null Return compiled code segment for the token when the token is custom helper
553
     */
554 448
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
555 448
        if (!isset($context['helpers'][$vars[0][0]])) {
556 338
            return;
557
        }
558
559 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
560 123
        $ch = array_shift($vars);
561 123
        $v = static::getVariableNames($context, $vars);
562 123
        static::addUsageCount($context, 'helpers', $ch[0]);
563 123
        $sep = $nosep ? '' : $context['ops']['seperator'];
564
565 123
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
566
    }
567
568
    /**
569
     * Return compiled PHP code for a handlebars else token
570
     *
571
     * @param array<string,array|string|integer> $context current compile context
572
     * @param array<boolean|integer|string|array> $vars parsed arguments list
573
     *
574
     * @return string Return compiled code segment for the token when the token is else
575
     */
576 47
    protected static function doElse(&$context, $vars) {
577 47
        switch ($context['stack'][count($context['stack']) - 2]) {
578
            case '[if]':
579 28
            case '[unless]':
580 22
                $context['stack'][] = ':';
581 22
                return "{$context['ops']['cnd_else']}";
582
            default:
583 28
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
584
        }
585
    }
586
587
    /**
588
     * Return compiled PHP code for a handlebars log token
589
     *
590
     * @param array<string,array|string|integer> $context current compile context
591
     * @param array<boolean|integer|string|array> $vars parsed arguments list
592
     * @param boolean $raw is this {{{ token or not
593
     *
594
     * @return string Return compiled code segment for the token
595
     */
596 1
    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...
597 1
        array_shift($vars);
598 1
        $v = static::getVariableNames($context, $vars);
599 1
        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...
600
    }
601
602
    /**
603
     * Return compiled PHP code for a handlebars lookup token
604
     *
605
     * @param array<string,array|string|integer> $context current compile context
606
     * @param array<boolean|integer|string|array> $vars parsed arguments list
607
     * @param boolean $raw is this {{{ token or not
608
     *
609
     * @return string Return compiled code segment for the token
610
     */
611 2
    protected static function compileLookup(&$context, &$vars, $raw) {
612 2
        $v2 = static::getVariableName($context, $vars[2]);
613 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...
614 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
615 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...
616
        } else {
617
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
618
        }
619
    }
620
621
    /**
622
     * Return compiled PHP code for template output
623
     *
624
     * @param array<string,array|string|integer> $context current compile context
625
     * @param string $variable PHP code for the variable
626
     * @param string $expression normalized handlebars expression
627
     * @param boolean $raw is this {{{ token or not
628
     * @param boolean $nosep true to compile without seperator
629
     *
630
     * @return string Return compiled code segment for the token
631
     */
632 459
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
633 459
        $sep = $nosep ? '' : $context['ops']['seperator'];
634 459
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
635 416
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
636
        } else {
637 43
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
638
        }
639
    }
640
641
    /**
642
     * Return compiled PHP code for a handlebars variable token
643
     *
644
     * @param array<string,array|string|integer> $context current compile context
645
     * @param array<boolean|integer|string|array> $vars parsed arguments list
646
     * @param boolean $raw is this {{{ token or not
647
     * @param boolean $nosep true to compile without seperator
648
     *
649
     * @return string Return compiled code segment for the token
650
     */
651 359
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
652 359
        if ($context['flags']['lambda']) {
653 227
            $V = array_shift($vars);
654 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
655
        } else {
656 132
            $v = static::getVariableName($context, $vars[0]);
657
        }
658 359
        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...
659
    }
660
661
    /**
662
     * Add usage count to context
663
     *
664
     * @param array<string,array|string|integer> $context current context
665
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
666
     * @param string $name used name
667
     * @param integer $count increment
668
     *
669
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
670
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
671
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
672
     */
673 583
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
674 583
        if (!isset($context['usedCount'][$category][$name])) {
675 583
            $context['usedCount'][$category][$name] = 0;
676
        }
677 583
        return ($context['usedCount'][$category][$name] += $count);
678
    }
679
}
680
681