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 ( cdc26c...a9fb08 )
by Zordius
02:57
created

Compiler::doElse()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 2
crap 3
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 732
    public static function compileTemplate(&$context, $template) {
44 732
        array_unshift($context['parsed'], array());
45 732
        Validator::verify($context, $template);
46
47 732
        if (count($context['error'])) {
48 75
            return;
49
        }
50
51 658
        Parser::setDelimiter($context);
52
53 658
        $context['compile'] = true;
54
55
        // Handle dynamic partials
56 658
        Partial::handleDynamic($context);
57
58
        // Do PHP code generation.
59 658
        $code = '';
60 658
        foreach ($context['parsed'][0] as $info) {
61 658
            if (is_array($info)) {
62 617
                $context['tokens']['current']++;
63 617
                $code .= "'" . static::compileToken($context, $info) . "'";
64
            } else {
65 658
                $code .= $info;
66
            }
67
        }
68
69 658
        static::$lastParsed = array_shift($context['parsed']);
70
71 658
        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 657
    public static function composePHPRender($context, $code) {
83 657
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
84 657
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
85 657
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
86 657
        $flagProp = Expression::boolString($context['flags']['prop']);
87 657
        $flagMethod = Expression::boolString($context['flags']['method']);
88 657
        $flagLambda = Expression::boolString($context['flags']['lambda']);
89 657
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
90 657
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
91 657
        $flagEcho = Expression::boolString($context['flags']['echo']);
92 657
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
93 657
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
94
95 657
        $constants = Exporter::constants($context);
96 657
        $helpers = Exporter::helpers($context);
97 657
        $partials = implode(",\n", $context['partialCode']);
98 657
        $debug = Runtime::DEBUG_ERROR_LOG;
99 657
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as {$context['runtimealias']};";
100 657
        $safeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] === 0)) ? "use {$context['safestring']} as SafeString;" : '';
101 657
        $exportSafeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] >0)) ? Exporter::safestring($context) : '';
102
103
        // Return generated PHP code string.
104
        return <<<VAREND
105 657
$safeString{$use}{$exportSafeString}return function (\$in = null, \$options = null) {
106 657
    \$helpers = $helpers;
107 657
    \$partials = array($partials);
108
    \$cx = array(
109
        'flags' => array(
110 657
            'jstrue' => $flagJStrue,
111 657
            'jsobj' => $flagJSObj,
112 657
            'spvar' => $flagSPVar,
113 657
            'prop' => $flagProp,
114 657
            'method' => $flagMethod,
115 657
            'lambda' => $flagLambda,
116 657
            'mustlok' => $flagMustlok,
117 657
            'mustlam' => $flagMustlam,
118 657
            'echo' => $flagEcho,
119 657
            'partnc' => $flagPartNC,
120 657
            'knohlp' => $flagKnownHlp,
121 657
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
122
        ),
123 657
        '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 657
        'runtime' => '{$context['runtime']}',
131
    );
132 657
    {$context['renderex']}
133 657
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
134 657
};
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 592
    protected static function getFuncName(&$context, $name, $tag) {
154 592
        static::addUsageCount($context, 'runtime', $name);
155
156 592
        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 590
            $dbg = '';
162
        }
163
164 592
        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 262
    protected static function getVariableNames(&$context, $vn, $blockParams = null) {
181 262
        $vars = array(array(), array());
182 262
        $exps = array();
183 262
        foreach ($vn as $i => $v) {
184 224
            $V = static::getVariableNameOrSubExpression($context, $v);
185 224
            if (is_string($i)) {
186 37
                $vars[1][] = "'$i'=>{$V[0]}";
187
            } else {
188 210
                $vars[0][] = $V[0];
189
            }
190 224
            $exps[] = $V[1];
191
        }
192 262
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
193 262
        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 373
    protected static function getVariableNameOrSubExpression(&$context, $var) {
223 373
        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('((is_array($in) && isset($in[\'true\'])) ? $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('((is_array($in) && isset($in[\'false\'])) ? $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('((is_array($in) && isset($in[\'2\'])) ? $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('((is_array($in) && isset($in[\'@index\'])) ? $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']) ? \$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']) ? \$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']) ? \$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']) ? \$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('((is_array($in) && isset($in[\'"a"\'])) ? $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('((is_array($in) && isset($in[\'a\'])) ? $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]) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-1]) && isset($cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\'])) ? $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]) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-3]) && isset($cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\'])) ? $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('((is_array($in) && isset($in[\'id\'])) ? $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 588
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null) {
256 588
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
257 82
            if ($var[1] === "undefined") {
258 2
                $var[1] = "null";
259
            }
260 82
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
261
        }
262
263 547
        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 547
        $exp = Expression::toString($levels, $spvar, $var);
265 547
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
266 547
        $is_array_check = $spvar ? false : true;
267
268
        // change base when trace to parent
269 547
        if ($levels > 0) {
270 40
            if ($spvar) {
271 2
                $base .= str_repeat("['_parent']", $levels);
272
            } else {
273 38
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
274
            }
275
        }
276
277 547
        if ((count($var) == 0) || (($var[0] === null) && (count($var) == 1))) {
278 135
            return array($base, $exp);
279
        }
280
281 490
        if ($var[0] === null) {
282 1
            array_shift($var);
283
        }
284
285
        // To support recursive context lookup, instance properties + methods and lambdas
286
        // the only way is using slower rendering time variable resolver.
287 490
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
288 386
            $L = $lookup ? ", $lookup[0]" : '';
289 386
            $A = $args ? ",$args[0]" : '';
290 386
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
291 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");
292
        }
293
294 105
        $n = Expression::arrayString($var);
295 105
        array_pop($var);
296 105
        $L = $lookup ? "[{$lookup[0]}]" : '';
297 105
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
298
299 105
        $checks = array();
300 105
        if ($levels > 0) {
301 10
            $checks[] = "isset($base)";
302
        }
303 105
        if ($is_array_check) {
304 102
            $checks[] = "is_array($base$p)";
305
        }
306 105
        $checks[] = "isset($base$n$L)";
307 105
        $check = ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '');
308
309 105
        return array("($check ? $base$n$L : " . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ')', $lookup ? "lookup $exp $lookup[1]" : $exp);
310
    }
311
312
    /**
313
     * Return compiled PHP code for a handlebars token
314
     *
315
     * @param array<string,array|string|integer> $context current compile context
316
     * @param array<string,array|boolean> $info parsed information
317
     *
318
     * @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...
319
     */
320 617
    protected static function compileToken(&$context, $info) {
321 617
        list($raw, $vars, $token, $indent) = $info;
322
323 617
        $context['tokens']['partialind'] = $indent;
324 617
        $context['currentToken'] = $token;
325
326 617
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
327 380
            return $ret;
328
        }
329
330 478
        if (isset($vars[0][0])) {
331 446
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
332 113
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
333
            }
334 340
            if ($vars[0][0] === 'else') {
335 28
                return static::doElse($context, $vars);
336
            }
337 323
            if ($vars[0][0] === 'lookup') {
338 2
                return static::compileLookup($context, $vars, $raw);
339
            }
340 321
            if ($vars[0][0] === 'log') {
341 2
                return static::compileLog($context, $vars, $raw);
342
            }
343
        }
344
345 356
        return static::compileVariable($context, $vars, $raw, false);
346
    }
347
348
    /**
349
     * handle partial
350
     *
351
     * @param array<string,array|string|integer> $context current compile context
352
     * @param array<boolean|integer|string|array> $vars parsed arguments list
353
     *
354
     * @return string Return compiled code segment for the partial
355
     */
356 88
    public static function partial(&$context, $vars) {
357 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...
358 88
        $pid = Parser::getPartialBlock($vars);
359 88
        $p = array_shift($vars);
360 88
        if ($context['flags']['runpart']) {
361 79
            if (!isset($vars[0])) {
362 72
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
363
            }
364 79
            $v = static::getVariableNames($context, $vars);
365 79
            $tag = ">$p[0] " .implode(' ', $v[1]);
366 79
            if (Parser::isSubExp($p)) {
367 4
                list($p) = static::compileSubExpression($context, $p[1]);
368
            } else {
369 75
                $p = "'$p[0]'";
370
            }
371 79
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
372 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...
373
        }
374 9
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
375
    }
376
377
    /**
378
     * handle inline partial
379
     *
380
     * @param array<string,array|string|integer> $context current compile context
381
     * @param array<boolean|integer|string|array> $vars parsed arguments list
382
     *
383
     * @return string Return compiled code segment for the partial
384
     */
385 10
    public static function inline(&$context, $vars) {
386 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...
387 10
        list($code) = array_shift($vars);
388 10
        $p = array_shift($vars);
389 10
        if (!isset($vars[0])) {
390 10
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
391
        }
392 10
        $v = static::getVariableNames($context, $vars);
393 10
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
394 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...
395
    }
396
397
    /**
398
     * Return compiled PHP code for a handlebars inverted section begin token
399
     *
400
     * @param array<string,array|string|integer> $context current compile context
401
     * @param array<boolean|integer|string|array> $vars parsed arguments list
402
     *
403
     * @return string Return compiled code segment for the token
404
     */
405 38
    protected static function invertedSection(&$context, $vars) {
406 38
        $v = static::getVariableName($context, $vars[0]);
407 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
408
    }
409
410
    /**
411
     * Return compiled PHP code for a handlebars block custom helper begin token
412
     *
413
     * @param array<string,array|string|integer> $context current compile context
414
     * @param array<boolean|integer|string|array> $vars parsed arguments list
415
     * @param boolean $inverted the logic will be inverted
416
     *
417
     * @return string Return compiled code segment for the token
418
     */
419 60
    protected static function blockCustomHelper(&$context, $vars, $inverted = false) {
420 60
        $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...
421 60
        $ch = array_shift($vars);
422 60
        $inverted = $inverted ? 'true' : 'false';
423 60
        static::addUsageCount($context, 'helpers', $ch[0]);
424 60
        $v = static::getVariableNames($context, $vars, $bp);
425
426 60
        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']}";
427
    }
428
429
    /**
430
     * Return compiled PHP code for a handlebars block end token
431
     *
432
     * @param array<string,array|string|integer> $context current compile context
433
     * @param array<boolean|integer|string|array> $vars parsed arguments list
434
     * @param string|null $matchop should also match to this operator
435
     *
436
     * @return string Return compiled code segment for the token
437
     */
438 321
    protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
439 321
        $pop = $context['stack'][count($context['stack']) - 1];
440 321
        $elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
441 321
        switch ($context['currentToken'][Token::POS_INNERTAG]) {
442 321
            case 'if':
443 268
            case 'unless':
444 71
                if ($pop === ':') {
445 19
                    array_pop($context['stack']);
446 19
                    return "$elsifend{$context['ops']['cnd_end']}";
447
                }
448 52
                if (!$context['flags']['nohbh']) {
449 50
                    return "{$context['ops']['cnd_else']}''$elsifend{$context['ops']['cnd_end']}";
450
                }
451 2
                break;
452 264
            case 'with':
453 33
                if (!$context['flags']['nohbh']) {
454 32
                    return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
455
                }
456
        }
457
458 242
        if ($pop === ':') {
459 2
            array_pop($context['stack']);
460 2
            return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
461
        }
462
463 240
        if ($elsifend !== '') {
464 3
            $elsifend = "{$context['ops']['cnd_else']}''$elsifend";
465
        }
466
467
        switch($pop) {
468 240
            case '#':
469 215
                return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
470 33
            case '^':
471 33
                return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
472
        }
473
    }
474
475
    /**
476
     * Return compiled PHP code for a handlebars block begin token
477
     *
478
     * @param array<string,array|string|integer> $context current compile context
479
     * @param array<boolean|integer|string|array> $vars parsed arguments list
480
     *
481
     * @return string Return compiled code segment for the token
482
     */
483 239
    protected static function blockBegin(&$context, $vars) {
484 239
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
485 239
        if (!$context['flags']['nohbh']) {
486 201
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
487 201
                case 'if':
488 66
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
489 66
                    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...
490 152
                case 'unless':
491 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...
492 149
                case 'each':
493 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...
494 104
                case 'with':
495 28
                    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...
496 28
                        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...
497
                    }
498
            }
499
        }
500
501 116
        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...
502
    }
503
504
    /**
505
     * compile {{#foo}} token
506
     *
507
     * @param array<string,array|string|integer> $context current compile context
508
     * @param array<boolean|integer|string|array> $vars parsed arguments list
509
     * @param boolean $isEach the section is #each
510
     *
511
     * @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...
512
     */
513 163
    protected static function section(&$context, $vars, $isEach = false) {
514 163
        $bs = 'null';
515 163
        $be = '';
516 163
        if ($isEach) {
517 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...
518 47
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
519 47
            $be = $bp ? " as |$bp[0] $bp[1]|" : '';
520 47
            array_shift($vars);
521
        }
522 163
        if ($context['flags']['lambda'] && !$isEach) {
523 69
            $V = array_shift($vars);
524 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
525
        } else {
526 94
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
527
        }
528 163
        $each = $isEach ? 'true' : 'false';
529 163
        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...
530
    }
531
532
    /**
533
     * compile {{with}} token
534
     *
535
     * @param array<string,array|string|integer> $context current compile context
536
     * @param array<boolean|integer|string|array> $vars parsed arguments list
537
     *
538
     * @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...
539
     */
540 28
    protected static function with(&$context, $vars) {
541 28
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
542 28
        $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...
543 28
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
544 28
        $be = $bp ? " as |$bp[0]|" : '';
545 28
        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...
546
    }
547
548
    /**
549
     * Return compiled PHP code for a handlebars custom helper token
550
     *
551
     * @param array<string,array|string|integer> $context current compile context
552
     * @param array<boolean|integer|string|array> $vars parsed arguments list
553
     * @param boolean $raw is this {{{ token or not
554
     * @param boolean $nosep true to compile without seperator
555
     *
556
     * @return string|null Return compiled code segment for the token when the token is custom helper
557
     */
558 454
    protected static function customHelper(&$context, $vars, $raw, $nosep) {
559 454
        if (!isset($context['helpers'][$vars[0][0]])) {
560 343
            return;
561
        }
562
563 124
        $fn = $raw ? 'raw' : $context['ops']['enc'];
564 124
        $ch = array_shift($vars);
565 124
        $v = static::getVariableNames($context, $vars);
566 124
        static::addUsageCount($context, 'helpers', $ch[0]);
567 124
        $sep = $nosep ? '' : $context['ops']['seperator'];
568
569 124
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
570
    }
571
572
    /**
573
     * Return compiled PHP code for a handlebars else token
574
     *
575
     * @param array<string,array|string|integer> $context current compile context
576
     * @param array<boolean|integer|string|array> $vars parsed arguments list
577
     *
578
     * @return string Return compiled code segment for the token when the token is else
579
     */
580 50
    protected static function doElse(&$context, $vars) {
581 50
        switch ($context['stack'][count($context['stack']) - 2]) {
582 50
            case '[if]':
583 29
            case '[unless]':
584 24
                $context['stack'][] = ':';
585 24
                return "{$context['ops']['cnd_else']}";
586
            default:
587 29
                return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}";
588
        }
589
    }
590
591
    /**
592
     * Return compiled PHP code for a handlebars log token
593
     *
594
     * @param array<string,array|string|integer> $context current compile context
595
     * @param array<boolean|integer|string|array> $vars parsed arguments list
596
     * @param boolean $raw is this {{{ token or not
597
     *
598
     * @return string Return compiled code segment for the token
599
     */
600 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...
601 2
        array_shift($vars);
602 2
        $v = static::getVariableNames($context, $vars);
603 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...
604
    }
605
606
    /**
607
     * Return compiled PHP code for a handlebars lookup token
608
     *
609
     * @param array<string,array|string|integer> $context current compile context
610
     * @param array<boolean|integer|string|array> $vars parsed arguments list
611
     * @param boolean $raw is this {{{ token or not
612
     *
613
     * @return string Return compiled code segment for the token
614
     */
615 2
    protected static function compileLookup(&$context, &$vars, $raw) {
616 2
        $v2 = static::getVariableName($context, $vars[2]);
617 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...
618 2
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
619 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...
620
        } else {
621
            return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}";
622
        }
623
    }
624
625
    /**
626
     * Return compiled PHP code for template output
627
     *
628
     * @param array<string,array|string|integer> $context current compile context
629
     * @param string $variable PHP code for the variable
630
     * @param string $expression normalized handlebars expression
631
     * @param boolean $raw is this {{{ token or not
632
     * @param boolean $nosep true to compile without seperator
633
     *
634
     * @return string Return compiled code segment for the token
635
     */
636 460
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep) {
637 460
        $sep = $nosep ? '' : $context['ops']['seperator'];
638 460
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
639 418
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
640
        } else {
641 42
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
642
        }
643
    }
644
645
    /**
646
     * Return compiled PHP code for a handlebars variable token
647
     *
648
     * @param array<string,array|string|integer> $context current compile context
649
     * @param array<boolean|integer|string|array> $vars parsed arguments list
650
     * @param boolean $raw is this {{{ token or not
651
     * @param boolean $nosep true to compile without seperator
652
     *
653
     * @return string Return compiled code segment for the token
654
     */
655 359
    protected static function compileVariable(&$context, &$vars, $raw, $nosep) {
656 359
        if ($context['flags']['lambda']) {
657 227
            $V = array_shift($vars);
658 227
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
659
        } else {
660 132
            $v = static::getVariableName($context, $vars[0]);
661
        }
662 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...
663
    }
664
665
    /**
666
     * Add usage count to context
667
     *
668
     * @param array<string,array|string|integer> $context current context
669
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
670
     * @param string $name used name
671
     * @param integer $count increment
672
     *
673
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
674
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
675
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
676
     */
677 592
    protected static function addUsageCount(&$context, $category, $name, $count = 1) {
678 592
        if (!isset($context['usedCount'][$category][$name])) {
679 592
            $context['usedCount'][$category][$name] = 0;
680
        }
681 592
        return ($context['usedCount'][$category][$name] += $count);
682
    }
683
}
684
685