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 ( 731f7f...62a245 )
by Zordius
06:59
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 723
    public static function compileTemplate(&$context, $template) {
44 723
        array_unshift($context['parsed'], array());
45 723
        Validator::verify($context, $template);
46
47 723
        if (count($context['error'])) {
48 75
            return;
49
        }
50
51
        // Do PHP code generation.
52 649
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 649
        Partial::handleDynamic($context);
56
57 649
        $code = '';
58 649
        foreach ($context['parsed'][0] as $info) {
59 649
            if (is_array($info)) {
60 608
                $context['tokens']['current']++;
61 608
                $code .= "'" . static::compileToken($context, $info) . "'";
62
            } else {
63 649
                $code .= $info;
64
            }
65
        }
66
67 649
        static::$lastParsed = array_shift($context['parsed']);
68
69 649
        return $code;
70
    }
71
72
    /**
73
     * Compose LightnCandy render codes for include()
74
     *
75
     * @param array<string,array|string|integer> $context Current context
76
     * @param string $code generated PHP code
77
     *
78
     * @return string Composed PHP code
79
     */
80 648
    public static function composePHPRender($context, $code) {
81 648
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
82 648
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
83 648
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
84 648
        $flagProp = Expression::boolString($context['flags']['prop']);
85 648
        $flagMethod = Expression::boolString($context['flags']['method']);
86 648
        $flagLambda = Expression::boolString($context['flags']['lambda']);
87 648
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
88 648
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
89 648
        $flagEcho = Expression::boolString($context['flags']['echo']);
90 648
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
91 648
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
92
93 648
        $constants = Exporter::constants($context);
94 648
        $helpers = Exporter::helpers($context);
95 648
        $partials = implode(",\n", $context['partialCode']);
96 648
        $debug = Runtime::DEBUG_ERROR_LOG;
97 648
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as LR;";
98
99
        // Return generated PHP code string.
100
        return <<<VAREND
101 648
use {$context['safestring']} as SafeString;{$use}return function (\$in, \$options = null) {
102
    \$helpers = $helpers;
103
    \$partials = array($partials);
104
    \$cx = array(
105
        'flags' => array(
106
            'jstrue' => $flagJStrue,
107
            'jsobj' => $flagJSObj,
108
            'spvar' => $flagSPVar,
109
            'prop' => $flagProp,
110
            'method' => $flagMethod,
111
            'lambda' => $flagLambda,
112
            'mustlok' => $flagMustlok,
113
            'mustlam' => $flagMustlam,
114
            'echo' => $flagEcho,
115
            'partnc' => $flagPartNC,
116
            'knohlp' => $flagKnownHlp,
117
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
118
        ),
119
        'constants' => $constants,
120
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
121
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
122
        'scopes' => array(),
123
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
124
        'blparam' => array(),
125 648
        'runtime' => '{$context['runtime']}',
126
    );
127 648
    {$context['renderex']}
128 648
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
129 648
};
130
VAREND
131
        ;
132
    }
133
134
    /**
135
     * Get function name for standalone or none standalone template.
136
     *
137
     * @param array<string,array|string|integer> $context Current context of compiler progress.
138
     * @param string $name base function name
139
     * @param string $tag original handlabars tag for debug
140
     *
141
     * @return string compiled Function name
142
     *
143
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test', ''
144
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
145
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'funcprefix' => 'lala_abc'), 'test3', ''
146
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'funcprefix' => 'haha456'), 'test', 'abc'
147
     */
148 585
    protected static function getFuncName(&$context, $name, $tag) {
149 585
        static::addUsageCount($context, 'runtime', $name);
150
151 585
        if ($context['flags']['debug'] && ($name != 'miss')) {
152 10
            $dbg = "'$tag', '$name', ";
153 10
            $name = 'debug';
154 10
            static::addUsageCount($context, 'runtime', 'debug');
155
        } else {
156 583
            $dbg = '';
157
        }
158
159 585
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "LR::$name($dbg";
160
    }
161
162
    /**
163
     * Get string presentation of variables
164
     *
165
     * @param array<string,array|string|integer> $context current compile context
166
     * @param array<array> $vn variable name array.
167
     * @param array<string>|null $blockParams block param list
168
     *
169
     * @return array<string|array> variable names
170
     *
171
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
172
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
173
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
174
     */
175 258
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
176 258
        $vars = array(array(), array());
177 258
        $exps = array();
178 258
        foreach ($vn as $i => $v) {
179 220
            $V = static::getVariableNameOrSubExpression($context, $v);
180 220
            if (is_string($i)) {
181 36
                $vars[1][] = "'$i'=>{$V[0]}";
182
            } else {
183 206
                $vars[0][] = $V[0];
184
            }
185 220
            $exps[] = $V[1];
186
        }
187 258
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
188 258
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
189
    }
190
191
    /**
192
     * Get string presentation of a sub expression
193
     *
194
     * @param array<string,array|string|integer> $context current compile context
195
     * @param array<boolean|integer|string|array> $vars parsed arguments list
196
     *
197
     * @return array<string> code representing passed expression
198
     */
199 41
    public static function compileSubExpression(&$context, $vars) {
200 41
        $ret = static::customHelper($context, $vars, true, true);
201
202 41
        if (($ret === null) && $context['flags']['lambda']) {
203 4
            $ret = static::compileVariable($context, $vars, true, true);
204
        }
205
206 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
207
    }
208
209
    /**
210
     * Get string presentation of a subexpression or a variable
211
     *
212
     * @param array<array|string|integer> $context current compile context
213
     * @param array<array|string|integer> $var variable parsed path
214
     *
215
     * @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...
216
     */
217 366
    protected static function getVariableNameOrSubExpression(&$context, $var) {
218 366
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
219
    }
220
221
    /**
222
     * Get string presentation of a variable
223
     *
224
     * @param array<array|string|integer> $var variable parsed path
225
     * @param array<array|string|integer> $context current compile context
226
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
227
     *
228
     * @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...
229
     *
230
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
231
     * @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')
232
     * @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')
233
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
234
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
235
     * @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')
236
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
237
     * @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')
238
     * @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')
239
     * @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')
240
     * @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')
241
     * @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')
242
     * @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"')
243
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
244
     * @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')
245
     * @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')
246
     * @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')
247
     * @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')
248
     * @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')
249
     */
250 581
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
251 581
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
252 79
            if ($var[1] === "undefined") {
253 2
                $var[1] = "null";
254
            }
255 79
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
256
        }
257
258 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...
259 542
        $exp = Expression::toString($levels, $spvar, $var);
260 542
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
261
262
        // change base when trace to parent
263 542
        if ($levels > 0) {
264 39
            if ($spvar) {
265 2
                $base .= str_repeat("['_parent']", $levels);
266
            } else {
267 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
268
            }
269
        }
270
271 542
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
272 133
            return array($base, $exp);
273
        }
274
275 486
        if ($var[0] === null) {
276 1
            array_shift($var);
277
        }
278
279
        // To support recursive context lookup, instance properties + methods and lambdas
280
        // the only way is using slower rendering time variable resolver.
281 486
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
282 386
            $L = $lookup ? ", $lookup[0]" : '';
283 386
            $A = $args ? ",$args[0]" : '';
284 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
285 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");
286
        }
287
288 101
        $n = Expression::arrayString($var);
289 101
        array_pop($var);
290 101
        $L = $lookup ? "[{$lookup[0]}]" : '';
291 101
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
292
293 101
        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);
294
    }
295
296
    /**
297
     * Return compiled PHP code for a handlebars token
298
     *
299
     * @param array<string,array|string|integer> $context current compile context
300
     * @param array<string,array|boolean> $info parsed information
301
     *
302
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer|double|string?

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

Loading history...
303
     */
304 608
    protected static function compileToken(&$context, $info) {
305 608
        list($raw, $vars, $token, $indent) = $info;
306
307 608
        $context['tokens']['partialind'] = $indent;
308 608
        $context['currentToken'] = $token;
309
310 608
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
311 372
            return $ret;
312
        }
313
314 473
        if (isset($vars[0][0])) {
315 441
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
316 112
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
317
            }
318 336
            if ($vars[0][0] === 'else') {
319 25
                return static::doElse($context, $vars);
320
            }
321 323
            if ($vars[0][0] === 'lookup') {
322 2
                return static::compileLookup($context, $vars, $raw);
323
            }
324 321
            if ($vars[0][0] === 'log') {
325 2
                return static::compileLog($context, $vars, $raw);
326
            }
327
        }
328
329 356
        return static::compileVariable($context, $vars, $raw, false);
330
    }
331
332
    /**
333
     * handle partial
334
     *
335
     * @param array<string,array|string|integer> $context current compile context
336
     * @param array<boolean|integer|string|array> $vars parsed arguments list
337
     *
338
     * @return string Return compiled code segment for the partial
339
     */
340 86
    public static function partial(&$context, $vars) {
341 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...
342 86
        $p = array_shift($vars);
343 86
        if ($context['flags']['runpart']) {
344 78
            if (!isset($vars[0])) {
345 71
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
346
            }
347 78
            $v = static::getVariableNames($context, $vars);
348 78
            $tag = ">$p[0] " .implode(' ', $v[1]);
349 78
            if (Parser::isSubExp($p)) {
350 4
                list($p) = static::compileSubExpression($context, $p[1]);
351
            } else {
352 74
                $p = "'$p[0]'";
353
            }
354 78
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
355 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...
356
        }
357 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...
358
    }
359
360
    /**
361
     * handle inline partial
362
     *
363
     * @param array<string,array|string|integer> $context current compile context
364
     * @param array<boolean|integer|string|array> $vars parsed arguments list
365
     *
366
     * @return string Return compiled code segment for the partial
367
     */
368 10
    public static function inline(&$context, $vars) {
369 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...
370 10
        list($code) = array_shift($vars);
371 10
        $p = array_shift($vars);
372 10
        if (!isset($vars[0])) {
373 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
374
        }
375 10
        $v = static::getVariableNames($context, $vars);
376 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
377 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...
378
    }
379
380
    /**
381
     * Return compiled PHP code for a handlebars inverted section begin token
382
     *
383
     * @param array<string,array|string|integer> $context current compile context
384
     * @param array<boolean|integer|string|array> $vars parsed arguments list
385
     *
386
     * @return string Return compiled code segment for the token
387
     */
388 38
    protected static function invertedSection(&$context, $vars) {
389 38
        $v = static::getVariableName($context, $vars[0]);
390 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
391
    }
392
393
    /**
394
     * Return compiled PHP code for a handlebars block custom helper begin token
395
     *
396
     * @param array<string,array|string|integer> $context current compile context
397
     * @param array<boolean|integer|string|array> $vars parsed arguments list
398
     * @param boolean $inverted the logic will be inverted
399
     *
400
     * @return string Return compiled code segment for the token
401
     */
402 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
403 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...
404 58
        $ch = array_shift($vars);
405 58
        $inverted = $inverted ? 'true' : 'false';
406 58
        static::addUsageCount($context, 'helpers', $ch[0]);
407 58
        $v = static::getVariableNames($context, $vars, $bp);
408
409 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']}";
410
    }
411
412
    /**
413
     * Return compiled PHP code for a handlebars block end token
414
     *
415
     * @param array<string,array|string|integer> $context current compile context
416
     * @param array<boolean|integer|string|array> $vars parsed arguments list
417
     * @param string|null $matchop should also match to this operator
418
     *
419
     * @return string Return compiled code segment for the token
420
     */
421 316
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
422 316
        $pop = $context['stack'][count($context['stack']) - 1];
423 316
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
424 316
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
425
            case 'if':
426
            case 'unless':
427 69
                if ($pop === ':') {
428 19
                    array_pop($context['stack']);
429 19
                    return "$elsifend{$context['ops']['cnd_end']}";
430
                }
431 50
                if (!$context['flags']['nohbh']) {
432 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
433
                }
434 2
                break;
435 236
            case 'with':
436 32
                if (!$context['flags']['nohbh']) {
437 31
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
438
                }
439
        }
440
441 239
        if ($pop === ':') {
442 2
            array_pop($context['stack']);
443 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
444
        }
445
446 237
        if ($elsifend !== '') {
447 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
448
        }
449
450
        switch($pop) {
451 237
            case '#':
452 212
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
453
            case '^':
454 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
455
        }
456
    }
457
458
    /**
459
     * Return compiled PHP code for a handlebars block begin token
460
     *
461
     * @param array<string,array|string|integer> $context current compile context
462
     * @param array<boolean|integer|string|array> $vars parsed arguments list
463
     *
464
     * @return string Return compiled code segment for the token
465
     */
466 235
    protected static function blockBegin(&$context, $vars) {
467 235
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
468 235
        if (!$context['flags']['nohbh']) {
469 197
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
470 4
                case 'if':
471 64
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
472 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...
473 4
                case 'unless':
474 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...
475 4
                case 'each':
476 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...
477 77
                case 'with':
478 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...
479 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...
480
                    }
481
            }
482
        }
483
484 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...
485
    }
486
487
    /**
488
     * compile {{#foo}} token
489
     *
490
     * @param array<string,array|string|integer> $context current compile context
491
     * @param array<boolean|integer|string|array> $vars parsed arguments list
492
     * @param boolean $isEach the section is #each
493
     *
494
     * @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...
495
     */
496 162
    protected static function section(&$context, $vars, $isEach = false) {
497 162
        $bs = 'null';
498 162
        $be = '';
499 162
        if ($isEach) {
500 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...
501 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
502 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
503 47
            array_shift($vars);
504
        }
505 162
        if ($context['flags']['lambda'] && !$isEach) {
506 69
            $V = array_shift($vars);
507 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
508
        } else {
509 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
510
        }
511 162
        $each = $isEach ? 'true' : 'false';
512 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...
513
    }
514
515
    /**
516
     * compile {{with}} token
517
     *
518
     * @param array<string,array|string|integer> $context current compile context
519
     * @param array<boolean|integer|string|array> $vars parsed arguments list
520
     *
521
     * @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...
522
     */
523 27
    protected static function with(&$context, $vars) {
524 27
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
525 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...
526 27
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
527 27
        $be = $bp ? " as |$bp[0]|" : '';
528 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...
529
    }
530
531
    /**
532
     * Return compiled PHP code for a handlebars custom helper token
533
     *
534
     * @param array<string,array|string|integer> $context current compile context
535
     * @param array<boolean|integer|string|array> $vars parsed arguments list
536
     * @param boolean $raw is this {{{ token or not
537
     * @param boolean $nosep true to compile without seperator
538
     *
539
     * @return string|null Return compiled code segment for the token when the token is custom helper
540
     */
541 449
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
542 449
        if (!isset($context['helpers'][$vars[0][0]])) {
543 339
            return;
544
        }
545
546 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
547 123
        $ch = array_shift($vars);
548 123
        $v = static::getVariableNames($context, $vars);
549 123
        static::addUsageCount($context, 'helpers', $ch[0]);
550 123
        $sep = $nosep ? '' : $context['ops']['seperator'];
551
552 123
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
553
    }
554
555
    /**
556
     * Return compiled PHP code for a handlebars else token
557
     *
558
     * @param array<string,array|string|integer> $context current compile context
559
     * @param array<boolean|integer|string|array> $vars parsed arguments list
560
     *
561
     * @return string Return compiled code segment for the token when the token is else
562
     */
563 47
    protected static function doElse(&$context, $vars) {
564 47
        switch ($context['stack'][count($context['stack']) - 2]) {
565
            case '[if]':
566 28
            case '[unless]':
567 22
                $context['stack'][] = ':';
568 22
                return "{$context['ops']['cnd_else']}";
569
            default:
570 28
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
571
        }
572
    }
573
574
    /**
575
     * Return compiled PHP code for a handlebars log token
576
     *
577
     * @param array<string,array|string|integer> $context current compile context
578
     * @param array<boolean|integer|string|array> $vars parsed arguments list
579
     * @param boolean $raw is this {{{ token or not
580
     *
581
     * @return string Return compiled code segment for the token
582
     */
583 2
    protected static function compileLog(&$context, &$vars, $raw) {
0 ignored issues
show
Unused Code introduced by
The parameter $raw is not used and could be removed.

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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