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 ( 8e76f6...eb031e )
by Zordius
02:19
created

Compiler   D

Complexity

Total Complexity 138

Size/Duplication

Total Lines 641
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 90.71%

Importance

Changes 41
Bugs 5 Features 3
Metric Value
wmc 138
c 41
b 5
f 3
lcom 1
cbo 6
dl 0
loc 641
ccs 244
cts 269
cp 0.9071
rs 4.7048

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getFuncName() 0 13 4
B compileTemplate() 0 34 5
A inline() 0 11 3
A composePHPRender() 0 52 2
A getVariableNames() 0 15 4
A compileSubExpression() 0 14 4
A getVariableNameOrSubExpression() 0 3 2
F getVariableName() 0 45 25
C compileToken() 0 32 8
C partial() 0 23 9
A invertedSection() 0 4 1
B blockCustomHelper() 0 11 5
C blockEnd() 0 36 12
B blockBegin() 0 20 11
C section() 0 21 10
A with() 0 7 4
B customHelper() 0 12 7
A doElse() 0 10 3
A compileLog() 0 5 1
B compileLookup() 0 9 7
B compileVariable() 0 13 9
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-2015 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file of LightnCandy Compiler
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
use \LightnCandy\Validator;
24
use \LightnCandy\Token;
25
use \LightnCandy\Expression;
26
use \LightnCandy\Parser;
27
28
/**
29
 * LightnCandy Compiler
30
 */
31
class Compiler extends Validator
32
{
33
    public static $lastParsed;
34
35
    /**
36
     * Compile template into PHP code
37
     *
38
     * @param array<string,array|string|integer> $context Current context
39
     * @param string $template handlebars template
40
     *
41
     * @return string|null generated PHP code
42
     */
43 720
    public static function compileTemplate(&$context, $template) {
44 720
        array_unshift($context['parsed'], array());
45 720
        Validator::verify($context, $template);
46
47 720
        if (count($context['error'])) {
48 70
            return;
49
        }
50
51
        // Do PHP code generation.
52 651
        Parser::setDelimiter($context);
53
54
        // Handle dynamic partials
55 651
        Partial::handleDynamic($context);
56
57 651
        $code = '';
58 651
        foreach ($context['parsed'][0] as $info) {
59 651
            if (is_array($info)) {
60 610
                $context['tokens']['current']++;
61 610
                $tmpl = static::compileToken($context, $info);
62 610
                if ($tmpl == $context['ops']['seperator']) {
63 1
                    $tmpl = '';
64
                } else {
65 609
                    $tmpl = "'$tmpl'";
66
                }
67 610
                $code .= $tmpl;
68
            } else {
69 651
                $code .= $info;
70
            }
71
        }
72
73 651
        static::$lastParsed = array_shift($context['parsed']);
74
75 651
        return $code;
76
    }
77
78
    /**
79
     * Compose LightnCandy render codes for include()
80
     *
81
     * @param array<string,array|string|integer> $context Current context
82
     * @param string $code generated PHP code
83
     *
84
     * @return string Composed PHP code
85
     */
86 650
    public static function composePHPRender($context, $code) {
87 650
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
88 650
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
89 650
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
90 650
        $flagProp = Expression::boolString($context['flags']['prop']);
91 650
        $flagMethod = Expression::boolString($context['flags']['method']);
92 650
        $flagLambda = Expression::boolString($context['flags']['lambda']);
93 650
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
94 650
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
95 650
        $flagEcho = Expression::boolString($context['flags']['echo']);
96 650
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
97 650
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
98
99 650
        $constants = Exporter::constants($context);
100 650
        $helpers = Exporter::helpers($context);
101 650
        $bhelpers = Exporter::helpers($context, 'blockhelpers');
102 650
        $hbhelpers = Exporter::helpers($context, 'hbhelpers');
103 650
        $partials = implode(",\n", $context['partialCode']);
104 650
        $debug = Runtime::DEBUG_ERROR_LOG;
105 650
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as LR;";
106
107
        // Return generated PHP code string.
108 650
        return "{$use}return function (\$in, \$options = null) {
109
    \$cx = array(
110
        'flags' => array(
111
            'jstrue' => $flagJStrue,
112
            'jsobj' => $flagJSObj,
113
            'spvar' => $flagSPVar,
114
            'prop' => $flagProp,
115
            'method' => $flagMethod,
116
            'lambda' => $flagLambda,
117
            'mustlok' => $flagMustlok,
118
            'mustlam' => $flagMustlam,
119
            'echo' => $flagEcho,
120
            'partnc' => $flagPartNC,
121
            'knohlp' => $flagKnownHlp,
122
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
123
        ),
124
        'constants' => $constants,
125
        'helpers' => $helpers,
126
        'blockhelpers' => $bhelpers,
127
        'hbhelpers' => isset(\$options['helpers']) ? array_merge($hbhelpers, \$options['helpers']) : $hbhelpers,
128
        'partials' => array($partials),
129
        'scopes' => array(),
130
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
131
        'blparam' => array(),
132 650
        'runtime' => '{$context['runtime']}',
133
    );
134 650
    {$context['renderex']}
135 650
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
136 650
};";
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'), 'test', ''
149
     * @expect 'LR::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime'), 'test2', ''
150
     * @expect "\$cx['funcs']['test3'](" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime'), 'test3', ''
151
     * @expect 'LR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime'), 'test', 'abc'
152
     */
153 586
    protected static function getFuncName(&$context, $name, $tag) {
154 586
        static::addUsageCount($context, 'runtime', $name);
155
156 586
        if ($context['flags']['debug'] && ($name != 'miss')) {
157 9
            $dbg = "'$tag', '$name', ";
158 9
            $name = 'debug';
159 9
            static::addUsageCount($context, 'runtime', 'debug');
160
        } else {
161 584
            $dbg = '';
162
        }
163
164 586
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "LR::$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 220
            $V = static::getVariableNameOrSubExpression($context, $v);
185 220
            if (is_string($i)) {
186 36
                $vars[1][] = "'$i'=>{$V[0]}";
187
            } else {
188 206
                $vars[0][] = $V[0];
189
            }
190 220
            $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
        $origSeperator = $context['ops']['seperator'];
206 41
        $context['ops']['seperator'] = '';
207
208 41
        $ret = static::customHelper($context, $vars, true);
209
210 41
        if (($ret === null) && $context['flags']['lambda']) {
211 4
            $ret = static::compileVariable($context, $vars, true);
212
        }
213
214 41
        $context['ops']['seperator'] = $origSeperator;
215
216 41
        return array($ret ? $ret : '', 'FIXME: $subExpression');
217
    }
218
219
    /**
220
     * Get string presentation of a subexpression or a variable
221
     *
222
     * @param array<array|string|integer> $context current compile context
223
     * @param array<array|string|integer> $var variable parsed path
224
     *
225
     * @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...
226
     */
227 366
    protected static function getVariableNameOrSubExpression(&$context, $var) {
228 366
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
229
    }
230
231
    /**
232
     * Get string presentation of a variable
233
     *
234
     * @param array<array|string|integer> $var variable parsed path
235
     * @param array<array|string|integer> $context current compile context
236
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
237
     *
238
     * @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...
239
     *
240
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
241
     * @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')
242
     * @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')
243
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
244
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
245
     * @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')
246
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
247
     * @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')
248
     * @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')
249
     * @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')
250
     * @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')
251
     * @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')
252
     * @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"')
253
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
254
     * @expect array('((isset($in[\'a\']) && is_array($in)) ? $in[\'a\'] : null)', '[a]') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0,'mustlam'=>0, 'lambda'=>0)), array('a')
255
     * @expect array('((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')
256
     * @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')
257
     * @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')
258
     * @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')
259
     */
260 581
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
261 581
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
262 79
            if ($var[1] === "undefined") {
263 2
                $var[1] = "null";
264
            }
265 79
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
266
        }
267
268 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...
269 542
        $exp = Expression::toString($levels, $spvar, $var);
270 542
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
271
272
        // change base when trace to parent
273 542
        if ($levels > 0) {
274 39
            if ($spvar) {
275 2
                $base .= str_repeat("['_parent']", $levels);
276
            } else {
277 37
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
278
            }
279
        }
280
281 542
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
282 134
            return array($base, $exp);
283
        }
284
285 485
        if ($var[0] === null) {
286 1
            array_shift($var);
287
        }
288
289
        // To support recursive context lookup, instance properties + methods and lambdas
290
        // the only way is using slower rendering time variable resolver.
291 485
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
292 386
            $L = $lookup ? ", $lookup[0]" : '';
293 386
            $A = $args ? ",$args[0]" : '';
294 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
295 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");
296
        }
297
298 100
        $n = Expression::arrayString($var);
299 100
        array_pop($var);
300 100
        $L = $lookup ? "[{$lookup[0]}]" : '';
301 100
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
302
303 100
        return array("((isset($base$n$L) && is_array($base$p)) ? $base$n$L : " . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ')', $lookup ? "lookup $exp $lookup[1]" : $exp);
304
    }
305
306
    /**
307
     * Return compiled PHP code for a handlebars token
308
     *
309
     * @param array<string,array|string|integer> $context current compile context
310
     * @param array<string,array|boolean> $info parsed information
311
     *
312
     * @return string Return compiled code segment for the token
0 ignored issues
show
Documentation introduced by
Should the return type not be string|boolean|integer|double?

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

Loading history...
313
     */
314 610
    protected static function compileToken(&$context, $info) {
315 610
        list($raw, $vars, $token, $indent) = $info;
316
317 610
        $context['tokens']['partialind'] = $indent;
318 610
        $context['currentToken'] = $token;
319
320
        // Do not touch the tag, keep it as is.
321 610
        if ($raw === -1) {
322
            return ".'" . Token::toString($token) . "'.";
323
        }
324
325 610
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
326 375
            return $ret;
327
        }
328
329 474
        if (isset($vars[0][0])) {
330 442
            if ($ret = static::customHelper($context, $vars, $raw)) {
331 112
                return $ret;
332
            }
333 337
            if ($vars[0][0] === 'else') {
334 27
                return static::doElse($context, $vars);
335
            }
336 322
            if ($vars[0][0] === 'lookup') {
337 2
                return static::compileLookup($context, $vars, $raw);
338
            }
339 320
            if ($vars[0][0] === 'log') {
340 1
                return static::compileLog($context, $vars, $raw);
341
            }
342
        }
343
344 358
        return static::compileVariable($context, $vars, $raw);
345
    }
346
347
    /**
348
     * handle partial
349
     *
350
     * @param array<string,array|string|integer> $context current compile context
351
     * @param array<boolean|integer|string|array> $vars parsed arguments list
352
     *
353
     * @return string Return compiled code segment for the partial
354
     */
355 86
    public static function partial(&$context, $vars) {
356
        // mustache spec: ignore missing partial
357 86
        if (($context['usedFeature']['dynpartial'] === 0) && ($context['usedFeature']['inlpartial'] === 0) && !isset($context['usedPartial'][$vars[0][0]])) {
358 1
            return $context['ops']['seperator'];
359
        }
360 85
        Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
361 85
        $p = array_shift($vars);
362 85
        if ($context['flags']['runpart']) {
363 77
            if (!isset($vars[0])) {
364 70
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
365
            }
366 77
            $v = static::getVariableNames($context, $vars);
367 77
            $tag = ">$p[0] " .implode(' ', $v[1]);
368 77
            if (Parser::isSubExp($p)) {
369 4
                list($p) = static::compileSubExpression($context, $p[1]);
370
            } else {
371 73
                $p = "'$p[0]'";
372
            }
373 77
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
374 77
            return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, $p, $v[0]$sp){$context['ops']['seperator']}";
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $context['ops']['...['ops']['seperator']}"; (string) is incompatible with the return type of the parent method LightnCandy\Validator::partial of type integer|double|boolean.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
375
        }
376 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...
377
    }
378
379
    /**
380
     * handle inline partial
381
     *
382
     * @param array<string,array|string|integer> $context current compile context
383
     * @param array<boolean|integer|string|array> $vars parsed arguments list
384
     *
385
     * @return string Return compiled code segment for the partial
386
     */
387 10
    public static function inline(&$context, $vars) {
388 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...
389 10
        list($code) = array_shift($vars);
390 10
        $p = array_shift($vars);
391 10
        if (!isset($vars[0])) {
392 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
393
        }
394 10
        $v = static::getVariableNames($context, $vars);
395 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
396 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...
397
    }
398
399
    /**
400
     * Return compiled PHP code for a handlebars inverted section 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
     *
405
     * @return string Return compiled code segment for the token
406
     */
407 38
    protected static function invertedSection(&$context, $vars) {
408 38
        $v = static::getVariableName($context, $vars[0]);
409 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
410
    }
411
412
    /**
413
     * Return compiled PHP code for a handlebars block custom helper begin 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 boolean $inverted the logic will be inverted
418
     *
419
     * @return string Return compiled code segment for the token
420
     */
421 61
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
422 61
        $notHBCH = !isset($context['hbhelpers'][$vars[0][0]]);
423
424 61
        $bp = Parser::getBlockParams($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<integer,boolean|integer|string|array>, but the function expects a array<integer,boolean|integer|array>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
425 61
        $ch = array_shift($vars);
426 61
        $inverted = $inverted ? 'true' : 'false';
427 61
        static::addUsageCount($context, $notHBCH ? 'blockhelpers' : 'hbhelpers', $ch[0]);
428 61
        $v = static::getVariableNames($context, $vars, $bp);
429
430 61
        return $context['ops']['seperator'] . static::getFuncName($context, $notHBCH ? 'bch' : 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}";
431
    }
432
433
    /**
434
     * Return compiled PHP code for a handlebars block end token
435
     *
436
     * @param array<string,array|string|integer> $context current compile context
437
     * @param array<boolean|integer|string|array> $vars parsed arguments list
438
     * @param string|null $matchop should also match to this operator
439
     *
440
     * @return string Return compiled code segment for the token
441
     */
442 319
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
443 319
        $pop = $context['stack'][count($context['stack']) - 1];
444 319
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
445 319
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
446
            case 'if':
447
            case 'unless':
448 69
                if ($pop === ':') {
449 19
                    array_pop($context['stack']);
450 19
                    return "$elsifend{$context['ops']['cnd_end']}";
451
                }
452 50
                if (!$context['flags']['nohbh']) {
453 48
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
454
                }
455 2
                break;
456 239
            case 'with':
457 32
                if (!$context['flags']['nohbh']) {
458 31
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
459
                }
460
        }
461
462 242
        if ($pop === ':') {
463 2
            array_pop($context['stack']);
464 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
465
        }
466
467 240
        if ($elsifend !== '') {
468 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
469
        }
470
471
        switch($pop) {
472 240
            case '#':
473 215
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
474
            case '^':
475 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
476
        }
477
    }
478
479
    /**
480
     * Return compiled PHP code for a handlebars block begin token
481
     *
482
     * @param array<string,array|string|integer> $context current compile context
483
     * @param array<boolean|integer|string|array> $vars parsed arguments list
484
     *
485
     * @return string Return compiled code segment for the token
486
     */
487 235
    protected static function blockBegin(&$context, $vars) {
488 235
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
489 235
        if (!$context['flags']['nohbh']) {
490 197
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
491 4
                case 'if':
492 64
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
493 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...
494 4
                case 'unless':
495 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...
496 4
                case 'each':
497 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...
498 77
                case 'with':
499 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...
500 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...
501
                    }
502
            }
503
        }
504
505 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...
506
    }
507
508
    /**
509
     * compile {{#foo}} token
510
     *
511
     * @param array<string,array|string|integer> $context current compile context
512
     * @param array<boolean|integer|string|array> $vars parsed arguments list
513
     * @param boolean $isEach the section is #each
514
     *
515
     * @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...
516
     */
517 162
    protected static function section(&$context, $vars, $isEach = false) {
518 162
        $bs = 'null';
519 162
        $be = '';
520 162
        if ($isEach) {
521 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...
522 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
523 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
524 47
            array_shift($vars);
525 47
            if (!isset($vars[0])) {
526
                $vars[0] = array(null);
527
            }
528
        }
529 162
        if ($context['flags']['lambda'] && !$isEach) {
530 69
            $V = array_shift($vars);
531 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
532
        } else {
533 93
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
534
        }
535 162
        $each = $isEach ? 'true' : 'false';
536 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...
537
    }
538
539
    /**
540
     * compile {{with}} token
541
     *
542
     * @param array<string,array|string|integer> $context current compile context
543
     * @param array<boolean|integer|string|array> $vars parsed arguments list
544
     *
545
     * @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...
546
     */
547 27
    protected static function with(&$context, $vars) {
548 27
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
549 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...
550 27
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
551 27
        $be = $bp ? " as |$bp[0]|" : '';
552 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...
553
    }
554
555
    /**
556
     * Return compiled PHP code for a handlebars custom helper 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
     * @param boolean $raw is this {{{ token or not
561
     *
562
     * @return string|null Return compiled code segment for the token when the token is custom helper
563
     */
564 450
    protected static function customHelper(&$context, $vars, $raw) {
565 450
        $notHH = !isset($context['hbhelpers'][$vars[0][0]]);
566 450
        if (!isset($context['helpers'][$vars[0][0]]) && $notHH) {
567 340
            return;
568
        }
569
570 123
        $fn = $raw ? 'raw' : $context['ops']['enc'];
571 123
        $ch = array_shift($vars);
572 123
        $v = static::getVariableNames($context, $vars);
573 123
        static::addUsageCount($context, $notHH ? 'helpers' : 'hbhelpers', $ch[0]);
574 123
        return $context['ops']['seperator'] . static::getFuncName($context, $notHH ? 'ch' : 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn'" . ($notHH ? '' : ', $in') . "){$context['ops']['seperator']}";
575
    }
576
577
    /**
578
     * Return compiled PHP code for a handlebars else token
579
     *
580
     * @param array<string,array|string|integer> $context current compile context
581
     * @param array<boolean|integer|string|array> $vars parsed arguments list
582
     *
583
     * @return string Return compiled code segment for the token when the token is else
584
     */
585 49
    protected static function doElse(&$context, $vars) {
586 49
        switch ($context['stack'][count($context['stack']) - 2]) {
587
            case '[if]':
588 30
            case '[unless]':
589 22
                $context['stack'][] = ':';
590 22
                return "{$context['ops']['cnd_else']}";
591
            default:
592 30
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
593
        }
594
    }
595
596
    /**
597
     * Return compiled PHP code for a handlebars log token
598
     *
599
     * @param array<string,array|string|integer> $context current compile context
600
     * @param array<boolean|integer|string|array> $vars parsed arguments list
601
     * @param boolean $raw is this {{{ token or not
602
     *
603
     * @return string Return compiled code segment for the token
604
     */
605 1
    protected static function compileLog(&$context, &$vars, $raw) {
0 ignored issues
show
Unused Code introduced by
The parameter $raw is not used and could be removed.

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
609
    }
610
611
    /**
612
     * Return compiled PHP code for a handlebars lookup token
613
     *
614
     * @param array<string,array|string|integer> $context current compile context
615
     * @param array<boolean|integer|string|array> $vars parsed arguments list
616
     * @param boolean $raw is this {{{ token or not
617
     *
618
     * @return string Return compiled code segment for the token
619
     */
620 2
    protected static function compileLookup(&$context, &$vars, $raw) {
621 2
        $v2 = static::getVariableName($context, $vars[2]);
622 2
        $v = static::getVariableName($context, $vars[1], $v2);
0 ignored issues
show
Documentation introduced by
$v2 is of type array<integer,array|stri...teger,string>|string"}>, but the function expects a array<integer,string>|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
623 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
624 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...
625
        } else {
626
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
627
        }
628
    }
629
630
    /**
631
     * Return compiled PHP code for a handlebars variable token
632
     *
633
     * @param array<string,array|string|integer> $context current compile context
634
     * @param array<boolean|integer|string|array> $vars parsed arguments list
635
     * @param boolean $raw is this {{{ token or not
636
     *
637
     * @return string Return compiled code segment for the token
638
     */
639 361
    protected static function compileVariable(&$context, &$vars, $raw) {
640 361
        if ($context['flags']['lambda']) {
641 227
            $V = array_shift($vars);
642 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
643
        } else {
644 134
            $v = static::getVariableName($context, $vars[0]);
645
        }
646 361
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
647 326
            return $context['ops']['seperator'] . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}";
0 ignored issues
show
Bug introduced by
It seems like $v[1] can also be of type array<integer,string>; however, LightnCandy\Compiler::getFuncName() does only seem to accept string, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
648
        } else {
649 35
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
650
        }
651
    }
652
653
    /**
654
     * Add usage count to context
655
     *
656
     * @param array<string,array|string|integer> $context current context
657
     * @param string $category ctegory name, can be one of: 'var', 'helpers', 'blockhelpers'
658
     * @param string $name used name
659
     * @param integer $count increment
660
     *
661
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
662
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
663
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
664
     */
665 586
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
666 586
        if (!isset($context['usedCount'][$category][$name])) {
667 586
            $context['usedCount'][$category][$name] = 0;
668
        }
669 586
        return ($context['usedCount'][$category][$name] += $count);
670
    }
671
}
672
673