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 ( f24fac...facd84 )
by Zordius
07:29 queued 02:04
created

Compiler::getVariableNames()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 11
cts 11
cp 1
rs 9.2
cc 4
eloc 12
nc 6
nop 3
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 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
                    $tmpl = '';
64
                } else {
65 607
                    $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
    \$helpers = $helpers;
108
    \$partials = array($partials);
109
    \$cx = array(
110
        'flags' => array(
111
            'jstrue' => $flagJStrue,
112
            'jsobj' => $flagJSObj,
113
            'spvar' => $flagSPVar,
114
            'prop' => $flagProp,
115
            'method' => $flagMethod,
116
            'lambda' => $flagLambda,
117
            'mustlok' => $flagMustlok,
118
            'mustlam' => $flagMustlam,
119
            'echo' => $flagEcho,
120
            'partnc' => $flagPartNC,
121
            'knohlp' => $flagKnownHlp,
122
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
123
        ),
124
        'constants' => $constants,
125
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
126
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
127
        'scopes' => array(),
128
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
129
        'blparam' => array(),
130 647
        'runtime' => '{$context['runtime']}',
131
    );
132 647
    {$context['renderex']}
133 647
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
134 647
};";
135
    }
136
137
    /**
138
     * Get function name for standalone or none standalone template.
139
     *
140
     * @param array<string,array|string|integer> $context Current context of compiler progress.
141
     * @param string $name base function name
142
     * @param string $tag original handlabars tag for debug
143
     *
144
     * @return string compiled Function name
145
     *
146
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
147
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
148
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'funcprefix' => 'lala_abc'), 'test3', ''
149
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'funcprefix' => 'haha456'), 'test', 'abc'
150
     */
151 584
    protected static function getFuncName(&$context, $name, $tag) {
152 584
        static::addUsageCount($context, 'runtime', $name);
153
154 584
        if ($context['flags']['debug'] && ($name != 'miss')) {
155 10
            $dbg = "'$tag', '$name', ";
156 10
            $name = 'debug';
157 10
            static::addUsageCount($context, 'runtime', 'debug');
158
        } else {
159 582
            $dbg = '';
160
        }
161
162 584
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "LR::$name($dbg";
163
    }
164
165
    /**
166
     * Get string presentation of variables
167
     *
168
     * @param array<string,array|string|integer> $context current compile context
169
     * @param array<array> $vn variable name array.
170
     * @param array<string>|null $blockParams block param list
171
     *
172
     * @return array<string|array> variable names
173
     *
174
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
175
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
176
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
177
     */
178 257
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
179 257
        $vars = array(array(), array());
180 257
        $exps = array();
181 257
        foreach ($vn as $i => $v) {
182 219
            $V = static::getVariableNameOrSubExpression($context, $v);
183 219
            if (is_string($i)) {
184 36
                $vars[1][] = "'$i'=>{$V[0]}";
185
            } else {
186 205
                $vars[0][] = $V[0];
187
            }
188 219
            $exps[] = $V[1];
189
        }
190 257
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
191 257
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
192
    }
193
194
    /**
195
     * Get string presentation of a sub expression
196
     *
197
     * @param array<string,array|string|integer> $context current compile context
198
     * @param array<boolean|integer|string|array> $vars parsed arguments list
199
     *
200
     * @return array<string> code representing passed expression
201
     */
202 41
    public static function compileSubExpression(&$context, $vars) {
203 41
        $ret = static::customHelper($context, $vars, true, true);
204
205 41
        if (($ret === null) && $context['flags']['lambda']) {
206 4
            $ret = static::compileVariable($context, $vars, true, true);
207
        }
208
209 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
210
    }
211
212
    /**
213
     * Get string presentation of a subexpression or a variable
214
     *
215
     * @param array<array|string|integer> $context current compile context
216
     * @param array<array|string|integer> $var variable parsed path
217
     *
218
     * @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...
219
     */
220 365
    protected static function getVariableNameOrSubExpression(&$context, $var) {
221 365
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
222
    }
223
224
    /**
225
     * Get string presentation of a variable
226
     *
227
     * @param array<array|string|integer> $var variable parsed path
228
     * @param array<array|string|integer> $context current compile context
229
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
230
     *
231
     * @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...
232
     *
233
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
234
     * @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')
235
     * @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')
236
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
237
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
238
     * @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')
239
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
240
     * @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')
241
     * @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')
242
     * @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')
243
     * @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')
244
     * @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')
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('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
247
     * @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')
248
     * @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')
249
     * @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')
250
     * @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')
251
     * @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')
252
     */
253 580
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
254 580
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
255 79
            if ($var[1] === "undefined") {
256 2
                $var[1] = "null";
257
            }
258 79
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
259
        }
260
261 541
        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...
262 541
        $exp = Expression::toString($levels, $spvar, $var);
263 541
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
264
265
        // change base when trace to parent
266 541
        if ($levels > 0) {
267 39
            if ($spvar) {
268 2
                $base .= str_repeat("['_parent']", $levels);
269
            } else {
270 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
271
            }
272
        }
273
274 541
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
275 133
            return array($base, $exp);
276
        }
277
278 485
        if ($var[0] === null) {
279 1
            array_shift($var);
280
        }
281
282
        // To support recursive context lookup, instance properties + methods and lambdas
283
        // the only way is using slower rendering time variable resolver.
284 485
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
285 386
            $L = $lookup ? ", $lookup[0]" : '';
286 386
            $A = $args ? ",$args[0]" : '';
287 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
288 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");
289
        }
290
291 100
        $n = Expression::arrayString($var);
292 100
        array_pop($var);
293 100
        $L = $lookup ? "[{$lookup[0]}]" : '';
294 100
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
295
296 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);
297
    }
298
299
    /**
300
     * Return compiled PHP code for a handlebars token
301
     *
302
     * @param array<string,array|string|integer> $context current compile context
303
     * @param array<string,array|boolean> $info parsed information
304
     *
305
     * @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...
306
     */
307 607
    protected static function compileToken(&$context, $info) {
308 607
        list($raw, $vars, $token, $indent) = $info;
309
310 607
        $context['tokens']['partialind'] = $indent;
311 607
        $context['currentToken'] = $token;
312
313
        // Do not touch the tag, keep it as is.
314 607
        if ($raw === -1) {
315
            return ".'" . Token::toString($token) . "'.";
316
        }
317
318 607
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
319 372
            return $ret;
320
        }
321
322 472
        if (isset($vars[0][0])) {
323 440
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
324 112
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
325
            }
326 335
            if ($vars[0][0] === 'else') {
327 25
                return static::doElse($context, $vars);
328
            }
329 322
            if ($vars[0][0] === 'lookup') {
330 2
                return static::compileLookup($context, $vars, $raw);
331
            }
332 320
            if ($vars[0][0] === 'log') {
333 1
                return static::compileLog($context, $vars, $raw);
334
            }
335
        }
336
337 356
        return static::compileVariable($context, $vars, $raw, false);
338
    }
339
340
    /**
341
     * handle partial
342
     *
343
     * @param array<string,array|string|integer> $context current compile context
344
     * @param array<boolean|integer|string|array> $vars parsed arguments list
345
     *
346
     * @return string Return compiled code segment for the partial
347
     */
348 86
    public static function partial(&$context, $vars) {
349 86
        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...
350 86
        $p = array_shift($vars);
351 86
        if ($context['flags']['runpart']) {
352 78
            if (!isset($vars[0])) {
353 71
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
354
            }
355 78
            $v = static::getVariableNames($context, $vars);
356 78
            $tag = ">$p[0] " .implode(' ', $v[1]);
357 78
            if (Parser::isSubExp($p)) {
358 4
                list($p) = static::compileSubExpression($context, $p[1]);
359
            } else {
360 74
                $p = "'$p[0]'";
361
            }
362 78
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
363 78
            return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, $p, $v[0]$sp){$context['ops']['seperator']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...['ops']['seperator']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::partial of type integer|double|boolean.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
364
        }
365 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...
366
    }
367
368
    /**
369
     * handle inline partial
370
     *
371
     * @param array<string,array|string|integer> $context current compile context
372
     * @param array<boolean|integer|string|array> $vars parsed arguments list
373
     *
374
     * @return string Return compiled code segment for the partial
375
     */
376 10
    public static function inline(&$context, $vars) {
377 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...
378 10
        list($code) = array_shift($vars);
379 10
        $p = array_shift($vars);
380 10
        if (!isset($vars[0])) {
381 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
382
        }
383 10
        $v = static::getVariableNames($context, $vars);
384 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
385 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...
386
    }
387
388
    /**
389
     * Return compiled PHP code for a handlebars inverted section begin token
390
     *
391
     * @param array<string,array|string|integer> $context current compile context
392
     * @param array<boolean|integer|string|array> $vars parsed arguments list
393
     *
394
     * @return string Return compiled code segment for the token
395
     */
396 38
    protected static function invertedSection(&$context, $vars) {
397 38
        $v = static::getVariableName($context, $vars[0]);
398 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
399
    }
400
401
    /**
402
     * Return compiled PHP code for a handlebars block custom helper 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
     * @param boolean $inverted the logic will be inverted
407
     *
408
     * @return string Return compiled code segment for the token
409
     */
410 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
411 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...
412 58
        $ch = array_shift($vars);
413 58
        $inverted = $inverted ? 'true' : 'false';
414 58
        static::addUsageCount($context, 'helpers', $ch[0]);
415 58
        $v = static::getVariableNames($context, $vars, $bp);
416
417 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']}";
418
    }
419
420
    /**
421
     * Return compiled PHP code for a handlebars block end token
422
     *
423
     * @param array<string,array|string|integer> $context current compile context
424
     * @param array<boolean|integer|string|array> $vars parsed arguments list
425
     * @param string|null $matchop should also match to this operator
426
     *
427
     * @return string Return compiled code segment for the token
428
     */
429 316
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
430 316
        $pop = $context['stack'][count($context['stack']) - 1];
431 316
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
432 316
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
433
            case 'if':
434
            case 'unless':
435 69
                if ($pop === ':') {
436 19
                    array_pop($context['stack']);
437 19
                    return "$elsifend{$context['ops']['cnd_end']}";
438
                }
439 50
                if (!$context['flags']['nohbh']) {
440 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
441
                }
442 2
                break;
443 236
            case 'with':
444 32
                if (!$context['flags']['nohbh']) {
445 31
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
446
                }
447
        }
448
449 239
        if ($pop === ':') {
450 2
            array_pop($context['stack']);
451 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
452
        }
453
454 237
        if ($elsifend !== '') {
455 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
456
        }
457
458
        switch($pop) {
459 237
            case '#':
460 212
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
461
            case '^':
462 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
463
        }
464
    }
465
466
    /**
467
     * Return compiled PHP code for a handlebars block begin token
468
     *
469
     * @param array<string,array|string|integer> $context current compile context
470
     * @param array<boolean|integer|string|array> $vars parsed arguments list
471
     *
472
     * @return string Return compiled code segment for the token
473
     */
474 235
    protected static function blockBegin(&$context, $vars) {
475 235
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
476 235
        if (!$context['flags']['nohbh']) {
477 197
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
478 4
                case 'if':
479 64
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
480 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...
481 4
                case 'unless':
482 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...
483 4
                case 'each':
484 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...
485 77
                case 'with':
486 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...
487 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...
488
                    }
489
            }
490
        }
491
492 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...
493
    }
494
495
    /**
496
     * compile {{#foo}} token
497
     *
498
     * @param array<string,array|string|integer> $context current compile context
499
     * @param array<boolean|integer|string|array> $vars parsed arguments list
500
     * @param boolean $isEach the section is #each
501
     *
502
     * @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...
503
     */
504 162
    protected static function section(&$context, $vars, $isEach = false) {
505 162
        $bs = 'null';
506 162
        $be = '';
507 162
        if ($isEach) {
508 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...
509 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
510 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
511 47
            array_shift($vars);
512 47
            if (!isset($vars[0])) {
513
                $vars[0] = array(null);
514
            }
515
        }
516 162
        if ($context['flags']['lambda'] && !$isEach) {
517 69
            $V = array_shift($vars);
518 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
519
        } else {
520 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
521
        }
522 162
        $each = $isEach ? 'true' : 'false';
523 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...
524
    }
525
526
    /**
527
     * compile {{with}} token
528
     *
529
     * @param array<string,array|string|integer> $context current compile context
530
     * @param array<boolean|integer|string|array> $vars parsed arguments list
531
     *
532
     * @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...
533
     */
534 27
    protected static function with(&$context, $vars) {
535 27
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
536 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...
537 27
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
538 27
        $be = $bp ? " as |$bp[0]|" : '';
539 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...
540
    }
541
542
    /**
543
     * Return compiled PHP code for a handlebars custom helper token
544
     *
545
     * @param array<string,array|string|integer> $context current compile context
546
     * @param array<boolean|integer|string|array> $vars parsed arguments list
547
     * @param boolean $raw is this {{{ token or not
548
     * @param boolean $nosep true to compile without seperator
549
     *
550
     * @return string|null Return compiled code segment for the token when the token is custom helper
551
     */
552 448
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
553 448
        if (!isset($context['helpers'][$vars[0][0]])) {
554 338
            return;
555
        }
556
557 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
558 123
        $ch = array_shift($vars);
559 123
        $v = static::getVariableNames($context, $vars);
560 123
        static::addUsageCount($context, 'helpers', $ch[0]);
561 123
        $sep = $nosep ? '' : $context['ops']['seperator'];
562
563 123
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
564
    }
565
566
    /**
567
     * Return compiled PHP code for a handlebars else token
568
     *
569
     * @param array<string,array|string|integer> $context current compile context
570
     * @param array<boolean|integer|string|array> $vars parsed arguments list
571
     *
572
     * @return string Return compiled code segment for the token when the token is else
573
     */
574 47
    protected static function doElse(&$context, $vars) {
575 47
        switch ($context['stack'][count($context['stack']) - 2]) {
576
            case '[if]':
577 28
            case '[unless]':
578 22
                $context['stack'][] = ':';
579 22
                return "{$context['ops']['cnd_else']}";
580
            default:
581 28
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
582
        }
583
    }
584
585
    /**
586
     * Return compiled PHP code for a handlebars log token
587
     *
588
     * @param array<string,array|string|integer> $context current compile context
589
     * @param array<boolean|integer|string|array> $vars parsed arguments list
590
     * @param boolean $raw is this {{{ token or not
591
     *
592
     * @return string Return compiled code segment for the token
593
     */
594 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...
595 1
        array_shift($vars);
596 1
        $v = static::getVariableNames($context, $vars);
597 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...
598
    }
599
600
    /**
601
     * Return compiled PHP code for a handlebars lookup token
602
     *
603
     * @param array<string,array|string|integer> $context current compile context
604
     * @param array<boolean|integer|string|array> $vars parsed arguments list
605
     * @param boolean $raw is this {{{ token or not
606
     *
607
     * @return string Return compiled code segment for the token
608
     */
609 2
    protected static function compileLookup(&$context, &$vars, $raw) {
610 2
        $v2 = static::getVariableName($context, $vars[2]);
611 2
        $v = static::getVariableName($context, $vars[1], $v2);
0 ignored issues
show
Documentation introduced by
$v2 is of type array<integer,array|stri...rray<integer,string>"}>, but the function expects a array<integer,string>|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
612 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
613 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...
614
        } else {
615
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
616
        }
617
    }
618
619
    /**
620
     * Return compiled PHP code for template output
621
     *
622
     * @param array<string,array|string|integer> $context current compile context
623
     * @param string $variable PHP code for the variable
624
     * @param string $expression normalized handlebars expression
625
     * @param boolean $raw is this {{{ token or not
626
     * @param boolean $nosep true to compile without seperator
627
     *
628
     * @return string Return compiled code segment for the token
629
     */
630 459
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
631 459
        $sep = $nosep ? '' : $context['ops']['seperator'];
632 459
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
633 416
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
634
        } else {
635 43
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
636
        }
637
    }
638
639
    /**
640
     * Return compiled PHP code for a handlebars variable token
641
     *
642
     * @param array<string,array|string|integer> $context current compile context
643
     * @param array<boolean|integer|string|array> $vars parsed arguments list
644
     * @param boolean $raw is this {{{ token or not
645
     * @param boolean $nosep true to compile without seperator
646
     *
647
     * @return string Return compiled code segment for the token
648
     */
649 359
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
650 359
        if ($context['flags']['lambda']) {
651 227
            $V = array_shift($vars);
652 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
653
        } else {
654 132
            $v = static::getVariableName($context, $vars[0]);
655
        }
656 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...
657
    }
658
659
    /**
660
     * Add usage count to context
661
     *
662
     * @param array<string,array|string|integer> $context current context
663
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
664
     * @param string $name used name
665
     * @param integer $count increment
666
     *
667
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
668
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
669
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
670
     */
671 584
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
672 584
        if (!isset($context['usedCount'][$category][$name])) {
673 584
            $context['usedCount'][$category][$name] = 0;
674
        }
675 584
        return ($context['usedCount'][$category][$name] += $count);
676
    }
677
}
678
679