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 ( 24d0ed...428a72 )
by Zordius
03:29
created

Compiler   D

Complexity

Total Complexity 135

Size/Duplication

Total Lines 642
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 91.86%

Importance

Changes 38
Bugs 5 Features 0
Metric Value
wmc 135
c 38
b 5
f 0
lcom 1
cbo 5
dl 0
loc 642
ccs 237
cts 258
cp 0.9186
rs 4.7005

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getFuncName() 0 13 4
A getVariableNames() 0 15 4
B compileTemplate() 0 30 4
B composePHPRender() 0 56 6
A compileSubExpression() 0 9 4
A getVariableNameOrSubExpression() 0 3 2
F getVariableName() 0 45 25
C compileToken() 0 27 7
B partial() 0 20 7
A inline() 0 11 3
A invertedSection() 0 4 1
A blockCustomHelper() 0 9 3
C blockEnd() 0 36 12
B blockBegin() 0 20 11
B section() 0 18 9
A with() 0 7 4
A customHelper() 0 13 4
A doElse() 0 10 3
A compileLog() 0 5 1
B compileLookup() 0 9 7
B compileOutput() 0 8 9
A compileVariable() 0 9 3
A addUsageCount() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like Compiler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Compiler, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2016 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file of LightnCandy Compiler
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
use \LightnCandy\Validator;
24
use \LightnCandy\Token;
25
use \LightnCandy\Expression;
26
use \LightnCandy\Parser;
27
28
/**
29
 * LightnCandy Compiler
30
 */
31
class Compiler extends Validator
32
{
33
    public static $lastParsed;
34
35
    /**
36
     * Compile template into PHP code
37
     *
38
     * @param array<string,array|string|integer> $context Current context
39
     * @param string $template handlebars template
40
     *
41
     * @return string|null generated PHP code
42
     */
43 727
    public static function compileTemplate(&$context, $template) {
44 727
        array_unshift($context['parsed'], array());
45 727
        Validator::verify($context, $template);
46
47 727
        if (count($context['error'])) {
48 75
            return;
49
        }
50
51 653
        Parser::setDelimiter($context);
52
53 653
        $context['compile'] = true;
54
55
        // Handle dynamic partials
56 653
        Partial::handleDynamic($context);
57
58
        // Do PHP code generation.
59 653
        $code = '';
60 653
        foreach ($context['parsed'][0] as $info) {
61 653
            if (is_array($info)) {
62 612
                $context['tokens']['current']++;
63 612
                $code .= "'" . static::compileToken($context, $info) . "'";
64
            } else {
65 653
                $code .= $info;
66
            }
67
        }
68
69 653
        static::$lastParsed = array_shift($context['parsed']);
70
71 653
        return $code;
72
    }
73
74
    /**
75
     * Compose LightnCandy render codes for include()
76
     *
77
     * @param array<string,array|string|integer> $context Current context
78
     * @param string $code generated PHP code
79
     *
80
     * @return string Composed PHP code
81
     */
82 652
    public static function composePHPRender($context, $code) {
83 652
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
84 652
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
85 652
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
86 652
        $flagProp = Expression::boolString($context['flags']['prop']);
87 652
        $flagMethod = Expression::boolString($context['flags']['method']);
88 652
        $flagLambda = Expression::boolString($context['flags']['lambda']);
89 652
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
90 652
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
91 652
        $flagEcho = Expression::boolString($context['flags']['echo']);
92 652
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
93 652
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
94
95 652
        $constants = Exporter::constants($context);
96 652
        $helpers = Exporter::helpers($context);
97 652
        $partials = implode(",\n", $context['partialCode']);
98 652
        $debug = Runtime::DEBUG_ERROR_LOG;
99 652
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as {$context['runtimealias']};";
100 652
        $safeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] === 0)) ? "use {$context['safestring']} as SafeString;" : '';
101 652
        $exportSafeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] >0)) ? Exporter::safestring($context) : '';
102
103
        // Return generated PHP code string.
104
        return <<<VAREND
105 652
$safeString{$use}{$exportSafeString}return function (\$in = null, \$options = null) {
106
    \$helpers = $helpers;
107
    \$partials = array($partials);
108
    \$cx = array(
109
        'flags' => array(
110
            'jstrue' => $flagJStrue,
111
            'jsobj' => $flagJSObj,
112
            'spvar' => $flagSPVar,
113
            'prop' => $flagProp,
114
            'method' => $flagMethod,
115
            'lambda' => $flagLambda,
116
            'mustlok' => $flagMustlok,
117
            'mustlam' => $flagMustlam,
118
            'echo' => $flagEcho,
119
            'partnc' => $flagPartNC,
120
            'knohlp' => $flagKnownHlp,
121
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
122
        ),
123
        'constants' => $constants,
124
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
125
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
126
        'scopes' => array(),
127
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
128
        'blparam' => array(),
129
        'partialid' => 0,
130 652
        'runtime' => '{$context['runtime']}',
131
    );
132 652
    {$context['renderex']}
133 652
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
134 652
};
135
VAREND
136
        ;
137
    }
138
139
    /**
140
     * Get function name for standalone or none standalone template.
141
     *
142
     * @param array<string,array|string|integer> $context Current context of compiler progress.
143
     * @param string $name base function name
144
     * @param string $tag original handlabars tag for debug
145
     *
146
     * @return string compiled Function name
147
     *
148
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), 'test', ''
149
     * @expect 'LL::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LL'), 'test2', ''
150
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 0, 'funcprefix' => 'lala_abc'), 'test3', ''
151
     * @expect 'RR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'runtimealias' => 'RR', 'funcprefix' => 'haha456'), 'test', 'abc'
152
     */
153 587
    protected static function getFuncName(&$context, $name, $tag) {
154 587
        static::addUsageCount($context, 'runtime', $name);
155
156 587
        if ($context['flags']['debug'] && ($name != 'miss')) {
157 10
            $dbg = "'$tag', '$name', ";
158 10
            $name = 'debug';
159 10
            static::addUsageCount($context, 'runtime', 'debug');
160
        } else {
161 585
            $dbg = '';
162
        }
163
164 587
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "{$context['runtimealias']}::$name($dbg";
165
    }
166
167
    /**
168
     * Get string presentation of variables
169
     *
170
     * @param array<string,array|string|integer> $context current compile context
171
     * @param array<array> $vn variable name array.
172
     * @param array<string>|null $blockParams block param list
173
     *
174
     * @return array<string|array> variable names
175
     *
176
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
177
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
178
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
179
     */
180 259
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
181 259
        $vars = array(array(), array());
182 259
        $exps = array();
183 259
        foreach ($vn as $i => $v) {
184 221
            $V = static::getVariableNameOrSubExpression($context, $v);
185 221
            if (is_string($i)) {
186 37
                $vars[1][] = "'$i'=>{$V[0]}";
187
            } else {
188 207
                $vars[0][] = $V[0];
189
            }
190 221
            $exps[] = $V[1];
191
        }
192 259
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
193 259
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
194
    }
195
196
    /**
197
     * Get string presentation of a sub expression
198
     *
199
     * @param array<string,array|string|integer> $context current compile context
200
     * @param array<boolean|integer|string|array> $vars parsed arguments list
201
     *
202
     * @return array<string> code representing passed expression
203
     */
204 41
    public static function compileSubExpression(&$context, $vars) {
205 41
        $ret = static::customHelper($context, $vars, true, true);
206
207 41
        if (($ret === null) && $context['flags']['lambda']) {
208 4
            $ret = static::compileVariable($context, $vars, true, true);
209
        }
210
211 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
212
    }
213
214
    /**
215
     * Get string presentation of a subexpression or a variable
216
     *
217
     * @param array<array|string|integer> $context current compile context
218
     * @param array<array|string|integer> $var variable parsed path
219
     *
220
     * @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...
221
     */
222 368
    protected static function getVariableNameOrSubExpression(&$context, $var) {
223 368
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
224
    }
225
226
    /**
227
     * Get string presentation of a variable
228
     *
229
     * @param array<array|string|integer> $var variable parsed path
230
     * @param array<array|string|integer> $context current compile context
231
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
232
     *
233
     * @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...
234
     *
235
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
236
     * @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')
237
     * @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')
238
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
239
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
240
     * @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')
241
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
242
     * @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')
243
     * @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')
244
     * @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')
245
     * @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')
246
     * @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')
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('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
249
     * @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')
250
     * @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')
251
     * @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')
252
     * @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')
253
     * @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', 'runtimealias' => 'LR'), array(null, 'id')
254
     */
255 583
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
256 583
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
257 80
            if ($var[1] === "undefined") {
258 2
                $var[1] = "null";
259
            }
260 80
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
261
        }
262
263 544
        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...
264 544
        $exp = Expression::toString($levels, $spvar, $var);
265 544
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
266
267
        // change base when trace to parent
268 544
        if ($levels > 0) {
269 39
            if ($spvar) {
270 2
                $base .= str_repeat("['_parent']", $levels);
271
            } else {
272 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
273
            }
274
        }
275
276 544
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
277 134
            return array($base, $exp);
278
        }
279
280 488
        if ($var[0] === null) {
281 1
            array_shift($var);
282
        }
283
284
        // To support recursive context lookup, instance properties + methods and lambdas
285
        // the only way is using slower rendering time variable resolver.
286 488
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
287 386
            $L = $lookup ? ", $lookup[0]" : '';
288 386
            $A = $args ? ",$args[0]" : '';
289 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
290 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");
291
        }
292
293 103
        $n = Expression::arrayString($var);
294 103
        array_pop($var);
295 103
        $L = $lookup ? "[{$lookup[0]}]" : '';
296 103
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
297
298 103
        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);
299
    }
300
301
    /**
302
     * Return compiled PHP code for a handlebars token
303
     *
304
     * @param array<string,array|string|integer> $context current compile context
305
     * @param array<string,array|boolean> $info parsed information
306
     *
307
     * @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...
308
     */
309 612
    protected static function compileToken(&$context, $info) {
310 612
        list($raw, $vars, $token, $indent) = $info;
311
312 612
        $context['tokens']['partialind'] = $indent;
313 612
        $context['currentToken'] = $token;
314
315 612
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
316 376
            return $ret;
317
        }
318
319 475
        if (isset($vars[0][0])) {
320 443
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
321 112
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
322
            }
323 338
            if ($vars[0][0] === 'else') {
324 26
                return static::doElse($context, $vars);
325
            }
326 323
            if ($vars[0][0] === 'lookup') {
327 2
                return static::compileLookup($context, $vars, $raw);
328
            }
329 321
            if ($vars[0][0] === 'log') {
330 2
                return static::compileLog($context, $vars, $raw);
331
            }
332
        }
333
334 356
        return static::compileVariable($context, $vars, $raw, false);
335
    }
336
337
    /**
338
     * handle partial
339
     *
340
     * @param array<string,array|string|integer> $context current compile context
341
     * @param array<boolean|integer|string|array> $vars parsed arguments list
342
     *
343
     * @return string Return compiled code segment for the partial
344
     */
345 88
    public static function partial(&$context, $vars) {
346 88
        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...
347 88
        $pid = Parser::getPartialBlock($vars);
348 88
        $p = array_shift($vars);
349 88
        if ($context['flags']['runpart']) {
350 79
            if (!isset($vars[0])) {
351 72
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
352
            }
353 79
            $v = static::getVariableNames($context, $vars);
354 79
            $tag = ">$p[0] " .implode(' ', $v[1]);
355 79
            if (Parser::isSubExp($p)) {
356 4
                list($p) = static::compileSubExpression($context, $p[1]);
357
            } else {
358 75
                $p = "'$p[0]'";
359
            }
360 79
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
361 79
            return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, $p, $v[0],$pid$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...
362
        }
363 9
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
364
    }
365
366
    /**
367
     * handle inline partial
368
     *
369
     * @param array<string,array|string|integer> $context current compile context
370
     * @param array<boolean|integer|string|array> $vars parsed arguments list
371
     *
372
     * @return string Return compiled code segment for the partial
373
     */
374 10
    public static function inline(&$context, $vars) {
375 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...
376 10
        list($code) = array_shift($vars);
377 10
        $p = array_shift($vars);
378 10
        if (!isset($vars[0])) {
379 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
380
        }
381 10
        $v = static::getVariableNames($context, $vars);
382 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
383 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...
384
    }
385
386
    /**
387
     * Return compiled PHP code for a handlebars inverted section begin token
388
     *
389
     * @param array<string,array|string|integer> $context current compile context
390
     * @param array<boolean|integer|string|array> $vars parsed arguments list
391
     *
392
     * @return string Return compiled code segment for the token
393
     */
394 38
    protected static function invertedSection(&$context, $vars) {
395 38
        $v = static::getVariableName($context, $vars[0]);
396 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
397
    }
398
399
    /**
400
     * Return compiled PHP code for a handlebars block custom helper begin token
401
     *
402
     * @param array<string,array|string|integer> $context current compile context
403
     * @param array<boolean|integer|string|array> $vars parsed arguments list
404
     * @param boolean $inverted the logic will be inverted
405
     *
406
     * @return string Return compiled code segment for the token
407
     */
408 58
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
409 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...
410 58
        $ch = array_shift($vars);
411 58
        $inverted = $inverted ? 'true' : 'false';
412 58
        static::addUsageCount($context, 'helpers', $ch[0]);
413 58
        $v = static::getVariableNames($context, $vars, $bp);
414
415 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']}";
416
    }
417
418
    /**
419
     * Return compiled PHP code for a handlebars block end token
420
     *
421
     * @param array<string,array|string|integer> $context current compile context
422
     * @param array<boolean|integer|string|array> $vars parsed arguments list
423
     * @param string|null $matchop should also match to this operator
424
     *
425
     * @return string Return compiled code segment for the token
426
     */
427 317
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
428 317
        $pop = $context['stack'][count($context['stack']) - 1];
429 317
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
430 317
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
431
            case 'if':
432
            case 'unless':
433 70
                if ($pop === ':') {
434 19
                    array_pop($context['stack']);
435 19
                    return "$elsifend{$context['ops']['cnd_end']}";
436
                }
437 51
                if (!$context['flags']['nohbh']) {
438 49
                    return "{$context['ops']['cnd_else']}''$elsifend{$context['ops']['cnd_end']}";
439
                }
440 2
                break;
441 236
            case 'with':
442 31
                if (!$context['flags']['nohbh']) {
443 30
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
444
                }
445
        }
446
447 239
        if ($pop === ':') {
448 2
            array_pop($context['stack']);
449 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
450
        }
451
452 237
        if ($elsifend !== '') {
453 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
454
        }
455
456
        switch($pop) {
457 237
            case '#':
458 212
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
459
            case '^':
460 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
461
        }
462
    }
463
464
    /**
465
     * Return compiled PHP code for a handlebars block begin token
466
     *
467
     * @param array<string,array|string|integer> $context current compile context
468
     * @param array<boolean|integer|string|array> $vars parsed arguments list
469
     *
470
     * @return string Return compiled code segment for the token
471
     */
472 236
    protected static function blockBegin(&$context, $vars) {
473 236
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
474 236
        if (!$context['flags']['nohbh']) {
475 198
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
476 4
                case 'if':
477 65
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
478 65
                    return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]}, {$includeZero})){$context['ops']['cnd_then']}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

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

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
479 4
                case 'unless':
480 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...
481 4
                case 'each':
482 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...
483 77
                case 'with':
484 26
                    if ($r = static::with($context, $vars)) {
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, but the function expects a array<integer,boolean|integer|string|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
485 26
                        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $r; (string) is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
486
                    }
487
            }
488
        }
489
490 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...
491
    }
492
493
    /**
494
     * compile {{#foo}} token
495
     *
496
     * @param array<string,array|string|integer> $context current compile context
497
     * @param array<boolean|integer|string|array> $vars parsed arguments list
498
     * @param boolean $isEach the section is #each
499
     *
500
     * @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...
501
     */
502 162
    protected static function section(&$context, $vars, $isEach = false) {
503 162
        $bs = 'null';
504 162
        $be = '';
505 162
        if ($isEach) {
506 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...
507 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
508 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
509 47
            array_shift($vars);
510
        }
511 162
        if ($context['flags']['lambda'] && !$isEach) {
512 69
            $V = array_shift($vars);
513 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
514
        } else {
515 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
516
        }
517 162
        $each = $isEach ? 'true' : 'false';
518 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...
519
    }
520
521
    /**
522
     * compile {{with}} token
523
     *
524
     * @param array<string,array|string|integer> $context current compile context
525
     * @param array<boolean|integer|string|array> $vars parsed arguments list
526
     *
527
     * @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...
528
     */
529 26
    protected static function with(&$context, $vars) {
530 26
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
531 26
        $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|in...er|string|array|null"}>, but the function expects a array<integer,boolean|integer|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
532 26
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
533 26
        $be = $bp ? " as |$bp[0]|" : '';
534 26
        return $context['ops']['seperator'] . static::getFuncName($context, 'wi', 'with ' . $v[1] . $be) . "\$cx, {$v[0]}, $bs, \$in, function(\$cx, \$in) {{$context['ops']['f_start']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...xt['ops']['f_start']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::with of type boolean.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
535
    }
536
537
    /**
538
     * Return compiled PHP code for a handlebars custom helper token
539
     *
540
     * @param array<string,array|string|integer> $context current compile context
541
     * @param array<boolean|integer|string|array> $vars parsed arguments list
542
     * @param boolean $raw is this {{{ token or not
543
     * @param boolean $nosep true to compile without seperator
544
     *
545
     * @return string|null Return compiled code segment for the token when the token is custom helper
546
     */
547 451
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
548 451
        if (!isset($context['helpers'][$vars[0][0]])) {
549 341
            return;
550
        }
551
552 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
553 123
        $ch = array_shift($vars);
554 123
        $v = static::getVariableNames($context, $vars);
555 123
        static::addUsageCount($context, 'helpers', $ch[0]);
556 123
        $sep = $nosep ? '' : $context['ops']['seperator'];
557
558 123
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
559
    }
560
561
    /**
562
     * Return compiled PHP code for a handlebars else token
563
     *
564
     * @param array<string,array|string|integer> $context current compile context
565
     * @param array<boolean|integer|string|array> $vars parsed arguments list
566
     *
567
     * @return string Return compiled code segment for the token when the token is else
568
     */
569 48
    protected static function doElse(&$context, $vars) {
570 48
        switch ($context['stack'][count($context['stack']) - 2]) {
571
            case '[if]':
572 28
            case '[unless]':
573 23
                $context['stack'][] = ':';
574 23
                return "{$context['ops']['cnd_else']}";
575
            default:
576 28
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
577
        }
578
    }
579
580
    /**
581
     * Return compiled PHP code for a handlebars log token
582
     *
583
     * @param array<string,array|string|integer> $context current compile context
584
     * @param array<boolean|integer|string|array> $vars parsed arguments list
585
     * @param boolean $raw is this {{{ token or not
586
     *
587
     * @return string Return compiled code segment for the token
588
     */
589 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...
590 2
        array_shift($vars);
591 2
        $v = static::getVariableNames($context, $vars);
592 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...
593
    }
594
595
    /**
596
     * Return compiled PHP code for a handlebars lookup token
597
     *
598
     * @param array<string,array|string|integer> $context current compile context
599
     * @param array<boolean|integer|string|array> $vars parsed arguments list
600
     * @param boolean $raw is this {{{ token or not
601
     *
602
     * @return string Return compiled code segment for the token
603
     */
604 2
    protected static function compileLookup(&$context, &$vars, $raw) {
605 2
        $v2 = static::getVariableName($context, $vars[2]);
606 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...
607 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
608 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...
609
        } else {
610
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
611
        }
612
    }
613
614
    /**
615
     * Return compiled PHP code for template output
616
     *
617
     * @param array<string,array|string|integer> $context current compile context
618
     * @param string $variable PHP code for the variable
619
     * @param string $expression normalized handlebars expression
620
     * @param boolean $raw is this {{{ token or not
621
     * @param boolean $nosep true to compile without seperator
622
     *
623
     * @return string Return compiled code segment for the token
624
     */
625 459
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
626 459
        $sep = $nosep ? '' : $context['ops']['seperator'];
627 459
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
628 417
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
629
        } else {
630 42
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
631
        }
632
    }
633
634
    /**
635
     * Return compiled PHP code for a handlebars variable token
636
     *
637
     * @param array<string,array|string|integer> $context current compile context
638
     * @param array<boolean|integer|string|array> $vars parsed arguments list
639
     * @param boolean $raw is this {{{ token or not
640
     * @param boolean $nosep true to compile without seperator
641
     *
642
     * @return string Return compiled code segment for the token
643
     */
644 359
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
645 359
        if ($context['flags']['lambda']) {
646 227
            $V = array_shift($vars);
647 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
648
        } else {
649 132
            $v = static::getVariableName($context, $vars[0]);
650
        }
651 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...
652
    }
653
654
    /**
655
     * Add usage count to context
656
     *
657
     * @param array<string,array|string|integer> $context current context
658
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
659
     * @param string $name used name
660
     * @param integer $count increment
661
     *
662
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
663
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
664
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
665
     */
666 587
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
667 587
        if (!isset($context['usedCount'][$category][$name])) {
668 587
            $context['usedCount'][$category][$name] = 0;
669
        }
670 587
        return ($context['usedCount'][$category][$name] += $count);
671
    }
672
}
673
674