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 ( 5b9acd...8e76f6 )
by Zordius
04:37
created

Compiler::compileSubExpression()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.2
cc 4
eloc 8
nc 4
nop 2
crap 4
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 720
    public static function compileTemplate(&$context, $template) {
44 720
        array_unshift($context['parsed'], array());
45 720
        Validator::verify($context, $template);
46
47 720
        if (count($context['error'])) {
48 70
            return;
49
        }
50
51
        // Do PHP code generation.
52 651
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 651
        Partial::handleDynamic($context);
56
57 651
        $code = '';
58 651
        foreach ($context['parsed'][0] as $info) {
59 651
            if (is_array($info)) {
60 610
                $context['tokens']['current']++;
61 610
                $tmpl = static::compileToken($context, $info);
62 610
                if ($tmpl == $context['ops']['seperator']) {
63 1
                    $tmpl = '';
64
                } else {
65 609
                    $tmpl = "'$tmpl'";
66
                }
67 610
                $code .= $tmpl;
68
            } else {
69 651
                $code .= $info;
70
            }
71
        }
72
73 651
        static::$lastParsed = array_shift($context['parsed']);
74
75 651
        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 650
    public static function composePHPRender($context, $code) {
87 650
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
88 650
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
89 650
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
90 650
        $flagProp = Expression::boolString($context['flags']['prop']);
91 650
        $flagMethod = Expression::boolString($context['flags']['method']);
92 650
        $flagLambda = Expression::boolString($context['flags']['lambda']);
93 650
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
94 650
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
95 650
        $flagEcho = Expression::boolString($context['flags']['echo']);
96 650
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
97 650
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
98
99 650
        $libstr = Exporter::runtime($context);
100 650
        $constants = Exporter::constants($context);
101 650
        $helpers = Exporter::helpers($context);
102 650
        $bhelpers = Exporter::helpers($context, 'blockhelpers');
103 650
        $hbhelpers = Exporter::helpers($context, 'hbhelpers');
104 650
        $partials = implode(",\n", $context['partialCode']);
105 650
        $debug = Runtime::DEBUG_ERROR_LOG;
106
107
        // Return generated PHP code string.
108 650
        return "use {$context['runtime']} as LR;
109
return function (\$in, \$options = null) {
110
    \$cx = array(
111
        'flags' => array(
112
            'jstrue' => $flagJStrue,
113
            'jsobj' => $flagJSObj,
114
            'spvar' => $flagSPVar,
115
            'prop' => $flagProp,
116
            'method' => $flagMethod,
117
            'lambda' => $flagLambda,
118
            'mustlok' => $flagMustlok,
119
            'mustlam' => $flagMustlam,
120
            'echo' => $flagEcho,
121
            'partnc' => $flagPartNC,
122
            'knohlp' => $flagKnownHlp,
123
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
124
        ),
125
        'constants' => $constants,
126
        'helpers' => $helpers,
127
        'blockhelpers' => $bhelpers,
128
        'hbhelpers' => isset(\$options['helpers']) ? array_merge($hbhelpers, \$options['helpers']) : $hbhelpers,
129
        'partials' => array($partials),
130
        'scopes' => array(),
131
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
132
        'blparam' => array(),
133 650
        'runtime' => '{$context['runtime']}',
134
$libstr
135
    );
136 650
    {$context['renderex']}
137 650
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
138 650
};";
139
    }
140
141
    /**
142
     * Get function name for standalone or none standalone template.
143
     *
144
     * @param array<string,array|string|integer> $context Current context of compiler progress.
145
     * @param string $name base function name
146
     * @param string $tag original handlabars tag for debug
147
     *
148
     * @return string compiled Function name
149
     *
150
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
151
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
152
     * @expect "\$cx['funcs']['test3'](" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime'), 'test3', ''
153
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime'), 'test', 'abc'
154
     */
155 586
    protected static function getFuncName(&$context, $name, $tag) {
156 586
        static::addUsageCount($context, 'runtime', $name);
157
158 586
        if ($context['flags']['debug'] && ($name != 'miss')) {
159 10
            $dbg = "'$tag', '$name', ";
160 10
            $name = 'debug';
161 10
            static::addUsageCount($context, 'runtime', 'debug');
162
        } else {
163 584
            $dbg = '';
164
        }
165
166 586
        return $context['flags']['standalone'] ? "\$cx['funcs']['$name']($dbg" : "LR::$name($dbg";
167
    }
168
169
    /**
170
     * Get string presentation of variables
171
     *
172
     * @param array<string,array|string|integer> $context current compile context
173
     * @param array<array> $vn variable name array.
174
     * @param array<string>|null $blockParams block param list
175
     *
176
     * @return array<string|array> variable names
177
     *
178
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
179
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
180
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
181
     */
182 259
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
183 259
        $vars = array(array(), array());
184 259
        $exps = array();
185 259
        foreach ($vn as $i => $v) {
186 220
            $V = static::getVariableNameOrSubExpression($context, $v);
187 220
            if (is_string($i)) {
188 36
                $vars[1][] = "'$i'=>{$V[0]}";
189
            } else {
190 206
                $vars[0][] = $V[0];
191
            }
192 220
            $exps[] = $V[1];
193
        }
194 259
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
195 259
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
196
    }
197
198
    /**
199
     * Get string presentation of a sub expression
200
     *
201
     * @param array<string,array|string|integer> $context current compile context
202
     * @param array<boolean|integer|string|array> $vars parsed arguments list
203
     *
204
     * @return array<string> code representing passed expression
205
     */
206 41
    public static function compileSubExpression(&$context, $vars) {
207 41
        $origSeperator = $context['ops']['seperator'];
208 41
        $context['ops']['seperator'] = '';
209
210 41
        $ret = static::customHelper($context, $vars, true);
211
212 41
        if (($ret === null) && $context['flags']['lambda']) {
213 4
            $ret = static::compileVariable($context, $vars, true);
214
        }
215
216 41
        $context['ops']['seperator'] = $origSeperator;
217
218 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
219
    }
220
221
    /**
222
     * Get string presentation of a subexpression or a variable
223
     *
224
     * @param array<array|string|integer> $context current compile context
225
     * @param array<array|string|integer> $var variable parsed path
226
     *
227
     * @return array<string> variable names
0 ignored issues
show
Documentation introduced by
Should the return type not be array<array|string|integer>?

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

Loading history...
228
     */
229 366
    protected static function getVariableNameOrSubExpression(&$context, $var) {
230 366
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
231
    }
232
233
    /**
234
     * Get string presentation of a variable
235
     *
236
     * @param array<array|string|integer> $var variable parsed path
237
     * @param array<array|string|integer> $context current compile context
238
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
239
     *
240
     * @return array<string> variable names
0 ignored issues
show
Documentation introduced by
Should the return type not be array<array|string|integer>?

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

Loading history...
241
     *
242
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
243
     * @expect array('((isset($in[\'true\']) && is_array($in)) ? $in[\'true\'] : null)', '[true]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('true')
244
     * @expect array('((isset($in[\'false\']) && is_array($in)) ? $in[\'false\'] : null)', '[false]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('false')
245
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
246
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
247
     * @expect array('((isset($in[\'2\']) && is_array($in)) ? $in[\'2\'] : null)', '[2]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('2')
248
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
249
     * @expect array('((isset($in[\'@index\']) && is_array($in)) ? $in[\'@index\'] : null)', '[@index]') when input array('flags'=>array('spvar'=>false,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@index')
250
     * @expect array("((isset(\$cx['sp_vars']['index']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['index'] : null)", '@[index]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@index')
251
     * @expect array("((isset(\$cx['sp_vars']['key']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['key'] : null)", '@[key]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@key')
252
     * @expect array("((isset(\$cx['sp_vars']['first']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['first'] : null)", '@[first]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@first')
253
     * @expect array("((isset(\$cx['sp_vars']['last']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['last'] : null)", '@[last]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('@last')
254
     * @expect array('((isset($in[\'"a"\']) && is_array($in)) ? $in[\'"a"\'] : null)', '["a"]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('"a"')
255
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
256
     * @expect array('((isset($in[\'a\']) && is_array($in)) ? $in[\'a\'] : null)', '[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('a')
257
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-1])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\'] : null)', '../[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array(1,'a')
258
     * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-3])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\'] : null)', '../../../[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array(3,'a')
259
     * @expect array('((isset($in[\'id\']) && is_array($in)) ? $in[\'id\'] : null)', 'this.[id]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array(null, 'id')
260
     * @expect array('LR::v($cx, $in, isset($in) ? $in : null, array(\'id\'))', 'this.[id]') when input array('flags'=>array('prop'=>true,'spvar'=>true,'debug'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0,'standalone'=>0), 'runtime' => 'Runtime'), array(null, 'id')
261
     */
262 581
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
263 581
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
264 79
            if ($var[1] === "undefined") {
265 2
                $var[1] = "null";
266
            }
267 79
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
268
        }
269
270 542
        list($levels, $spvar, $var) = Expression::analyze($context, $var);
0 ignored issues
show
Documentation introduced by
$var is of type array<integer,array|stri...array|string|integer"}>, but the function expects a array<integer,array|string|integer>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
271 542
        $exp = Expression::toString($levels, $spvar, $var);
272 542
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
273
274
        // change base when trace to parent
275 542
        if ($levels > 0) {
276 39
            if ($spvar) {
277 2
                $base .= str_repeat("['_parent']", $levels);
278
            } else {
279 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
280
            }
281
        }
282
283 542
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
284 134
            return array($base, $exp);
285
        }
286
287 485
        if ($var[0] === null) {
288 1
            array_shift($var);
289
        }
290
291
        // To support recursive context lookup, instance properties + methods and lambdas
292
        // the only way is using slower rendering time variable resolver.
293 485
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
294 386
            $L = $lookup ? ", $lookup[0]" : '';
295 386
            $A = $args ? ",$args[0]" : '';
296 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
297 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");
298
        }
299
300 100
        $n = Expression::arrayString($var);
301 100
        array_pop($var);
302 100
        $L = $lookup ? "[{$lookup[0]}]" : '';
303 100
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
304
305 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);
306
    }
307
308
    /**
309
     * Return compiled PHP code for a handlebars token
310
     *
311
     * @param array<string,array|string|integer> $context current compile context
312
     * @param array<string,array|boolean> $info parsed information
313
     *
314
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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...
315
     */
316 610
    protected static function compileToken(&$context, $info) {
317 610
        list($raw, $vars, $token, $indent) = $info;
318
319 610
        $context['tokens']['partialind'] = $indent;
320 610
        $context['currentToken'] = $token;
321
322
        // Do not touch the tag, keep it as is.
323 610
        if ($raw === -1) {
324
            return ".'" . Token::toString($token) . "'.";
325
        }
326
327 610
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
328 375
            return $ret;
329
        }
330
331 474
        if (isset($vars[0][0])) {
332 442
            if ($ret = static::customHelper($context, $vars, $raw)) {
333 112
                return $ret;
334
            }
335 337
            if ($vars[0][0] === 'else') {
336 27
                return static::doElse($context, $vars);
337
            }
338 322
            if ($vars[0][0] === 'lookup') {
339 2
                return static::compileLookup($context, $vars, $raw);
340
            }
341 320
            if ($vars[0][0] === 'log') {
342 1
                return static::compileLog($context, $vars, $raw);
343
            }
344
        }
345
346 358
        return static::compileVariable($context, $vars, $raw);
347
    }
348
349
    /**
350
     * handle partial
351
     *
352
     * @param array<string,array|string|integer> $context current compile context
353
     * @param array<boolean|integer|string|array> $vars parsed arguments list
354
     *
355
     * @return string Return compiled code segment for the partial
356
     */
357 86
    public static function partial(&$context, $vars) {
358
        // mustache spec: ignore missing partial
359 86
        if (($context['usedFeature']['dynpartial'] === 0) && ($context['usedFeature']['inlpartial'] === 0) && !isset($context['usedPartial'][$vars[0][0]])) {
360 1
            return $context['ops']['seperator'];
361
        }
362 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...
363 85
        $p = array_shift($vars);
364 85
        if ($context['flags']['runpart']) {
365 77
            if (!isset($vars[0])) {
366 70
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
367
            }
368 77
            $v = static::getVariableNames($context, $vars);
369 77
            $tag = ">$p[0] " .implode(' ', $v[1]);
370 77
            if (Parser::isSubExp($p)) {
371 4
                list($p) = static::compileSubExpression($context, $p[1]);
372
            } else {
373 73
                $p = "'$p[0]'";
374
            }
375 77
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
376 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...
377
        }
378 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...
379
    }
380
381
    /**
382
     * handle inline partial
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 partial
388
     */
389 10
    public static function inline(&$context, $vars) {
390 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...
391 10
        list($code) = array_shift($vars);
392 10
        $p = array_shift($vars);
393 10
        if (!isset($vars[0])) {
394 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
395
        }
396 10
        $v = static::getVariableNames($context, $vars);
397 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
398 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...
399
    }
400
401
    /**
402
     * Return compiled PHP code for a handlebars inverted section begin token
403
     *
404
     * @param array<string,array|string|integer> $context current compile context
405
     * @param array<boolean|integer|string|array> $vars parsed arguments list
406
     *
407
     * @return string Return compiled code segment for the token
408
     */
409 38
    protected static function invertedSection(&$context, $vars) {
410 38
        $v = static::getVariableName($context, $vars[0]);
411 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
412
    }
413
414
    /**
415
     * Return compiled PHP code for a handlebars block custom helper begin token
416
     *
417
     * @param array<string,array|string|integer> $context current compile context
418
     * @param array<boolean|integer|string|array> $vars parsed arguments list
419
     * @param boolean $inverted the logic will be inverted
420
     *
421
     * @return string Return compiled code segment for the token
422
     */
423 61
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
424 61
        $notHBCH = !isset($context['hbhelpers'][$vars[0][0]]);
425
426 61
        $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
427 61
        $ch = array_shift($vars);
428 61
        $inverted = $inverted ? 'true' : 'false';
429 61
        static::addUsageCount($context, $notHBCH ? 'blockhelpers' : 'hbhelpers', $ch[0]);
430 61
        $v = static::getVariableNames($context, $vars, $bp);
431
432 61
        return $context['ops']['seperator'] . static::getFuncName($context, $notHBCH ? 'bch' : 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}";
433
    }
434
435
    /**
436
     * Return compiled PHP code for a handlebars block end token
437
     *
438
     * @param array<string,array|string|integer> $context current compile context
439
     * @param array<boolean|integer|string|array> $vars parsed arguments list
440
     * @param string|null $matchop should also match to this operator
441
     *
442
     * @return string Return compiled code segment for the token
443
     */
444 319
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
445 319
        $pop = $context['stack'][count($context['stack']) - 1];
446 319
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
447 319
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
448
            case 'if':
449
            case 'unless':
450 69
                if ($pop === ':') {
451 19
                    array_pop($context['stack']);
452 19
                    return "$elsifend{$context['ops']['cnd_end']}";
453
                }
454 50
                if (!$context['flags']['nohbh']) {
455 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
456
                }
457 2
                break;
458 239
            case 'with':
459 32
                if (!$context['flags']['nohbh']) {
460 31
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
461
                }
462
        }
463
464 242
        if ($pop === ':') {
465 2
            array_pop($context['stack']);
466 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
467
        }
468
469 240
        if ($elsifend !== '') {
470 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
471
        }
472
473
        switch($pop) {
474 240
            case '#':
475 215
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
476
            case '^':
477 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
478
        }
479
    }
480
481
    /**
482
     * Return compiled PHP code for a handlebars block begin token
483
     *
484
     * @param array<string,array|string|integer> $context current compile context
485
     * @param array<boolean|integer|string|array> $vars parsed arguments list
486
     *
487
     * @return string Return compiled code segment for the token
488
     */
489 235
    protected static function blockBegin(&$context, $vars) {
490 235
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
491 235
        if (!$context['flags']['nohbh']) {
492 197
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
493 4
                case 'if':
494 64
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
495 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...
496 4
                case 'unless':
497 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...
498 4
                case 'each':
499 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...
500 77
                case 'with':
501 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...
502 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...
503
                    }
504
            }
505
        }
506
507 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...
508
    }
509
510
    /**
511
     * compile {{#foo}} token
512
     *
513
     * @param array<string,array|string|integer> $context current compile context
514
     * @param array<boolean|integer|string|array> $vars parsed arguments list
515
     * @param boolean $isEach the section is #each
516
     *
517
     * @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...
518
     */
519 162
    protected static function section(&$context, $vars, $isEach = false) {
520 162
        $bs = 'null';
521 162
        $be = '';
522 162
        if ($isEach) {
523 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...
524 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
525 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
526 47
            array_shift($vars);
527 47
            if (!isset($vars[0])) {
528
                $vars[0] = array(null);
529
            }
530
        }
531 162
        if ($context['flags']['lambda'] && !$isEach) {
532 69
            $V = array_shift($vars);
533 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
534
        } else {
535 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
536
        }
537 162
        $each = $isEach ? 'true' : 'false';
538 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...
539
    }
540
541
    /**
542
     * compile {{with}} token
543
     *
544
     * @param array<string,array|string|integer> $context current compile context
545
     * @param array<boolean|integer|string|array> $vars parsed arguments list
546
     *
547
     * @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...
548
     */
549 27
    protected static function with(&$context, $vars) {
550 27
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
551 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...
552 27
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
553 27
        $be = $bp ? " as |$bp[0]|" : '';
554 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...
555
    }
556
557
    /**
558
     * Return compiled PHP code for a handlebars custom helper token
559
     *
560
     * @param array<string,array|string|integer> $context current compile context
561
     * @param array<boolean|integer|string|array> $vars parsed arguments list
562
     * @param boolean $raw is this {{{ token or not
563
     *
564
     * @return string|null Return compiled code segment for the token when the token is custom helper
565
     */
566 450
    protected static function customHelper(&$context, $vars, $raw) {
567 450
        $notHH = !isset($context['hbhelpers'][$vars[0][0]]);
568 450
        if (!isset($context['helpers'][$vars[0][0]]) && $notHH) {
569 340
            return;
570
        }
571
572 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
573 123
        $ch = array_shift($vars);
574 123
        $v = static::getVariableNames($context, $vars);
575 123
        static::addUsageCount($context, $notHH ? 'helpers' : 'hbhelpers', $ch[0]);
576 123
        return $context['ops']['seperator'] . static::getFuncName($context, $notHH ? 'ch' : 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn'" . ($notHH ? '' : ', $in') . "){$context['ops']['seperator']}";
577
    }
578
579
    /**
580
     * Return compiled PHP code for a handlebars else token
581
     *
582
     * @param array<string,array|string|integer> $context current compile context
583
     * @param array<boolean|integer|string|array> $vars parsed arguments list
584
     *
585
     * @return string Return compiled code segment for the token when the token is else
586
     */
587 49
    protected static function doElse(&$context, $vars) {
588 49
        switch ($context['stack'][count($context['stack']) - 2]) {
589
            case '[if]':
590 30
            case '[unless]':
591 22
                $context['stack'][] = ':';
592 22
                return "{$context['ops']['cnd_else']}";
593
            default:
594 30
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
595
        }
596
    }
597
598
    /**
599
     * Return compiled PHP code for a handlebars log token
600
     *
601
     * @param array<string,array|string|integer> $context current compile context
602
     * @param array<boolean|integer|string|array> $vars parsed arguments list
603
     * @param boolean $raw is this {{{ token or not
604
     *
605
     * @return string Return compiled code segment for the token
606
     */
607 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...
608 1
        array_shift($vars);
609 1
        $v = static::getVariableNames($context, $vars);
610 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...
611
    }
612
613
    /**
614
     * Return compiled PHP code for a handlebars lookup token
615
     *
616
     * @param array<string,array|string|integer> $context current compile context
617
     * @param array<boolean|integer|string|array> $vars parsed arguments list
618
     * @param boolean $raw is this {{{ token or not
619
     *
620
     * @return string Return compiled code segment for the token
621
     */
622 2
    protected static function compileLookup(&$context, &$vars, $raw) {
623 2
        $v2 = static::getVariableName($context, $vars[2]);
624 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...
625 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
626 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...
627
        } else {
628
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
629
        }
630
    }
631
632
    /**
633
     * Return compiled PHP code for a handlebars variable token
634
     *
635
     * @param array<string,array|string|integer> $context current compile context
636
     * @param array<boolean|integer|string|array> $vars parsed arguments list
637
     * @param boolean $raw is this {{{ token or not
638
     *
639
     * @return string Return compiled code segment for the token
640
     */
641 361
    protected static function compileVariable(&$context, &$vars, $raw) {
642 361
        if ($context['flags']['lambda']) {
643 227
            $V = array_shift($vars);
644 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
645
        } else {
646 134
            $v = static::getVariableName($context, $vars[0]);
647
        }
648 361
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
649 326
            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...
650
        } else {
651 35
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
652
        }
653
    }
654
655
    /**
656
     * Add usage count to context
657
     *
658
     * @param array<string,array|string|integer> $context current context
659
     * @param string $category ctegory name, can be one of: 'var', 'helpers', 'blockhelpers'
660
     * @param string $name used name
661
     * @param integer $count increment
662
     *
663
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
664
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
665
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
666
     */
667 586
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
668 586
        if (!isset($context['usedCount'][$category][$name])) {
669 586
            $context['usedCount'][$category][$name] = 0;
670
        }
671 586
        return ($context['usedCount'][$category][$name] += $count);
672
    }
673
}
674
675