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.

Compiler   F
last analyzed

Complexity

Total Complexity 160

Size/Duplication

Total Lines 710
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.96%

Importance

Changes 0
Metric Value
wmc 160
lcom 1
cbo 5
dl 0
loc 710
ccs 285
cts 288
cp 0.9896
rs 1.89
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A compileTemplate() 0 32 4
B composePHPRender() 0 62 8
A getFuncName() 0 14 4
A getVariableNames() 0 16 4
A compileSubExpression() 0 10 4
A getVariableNameOrSubExpression() 0 4 2
F getVariableName() 0 74 41
B compileToken() 0 28 8
B partial() 0 21 7
A inline() 0 12 3
A invertedSection() 0 5 1
A blockCustomHelper() 0 10 3
B blockEnd() 0 33 11
B blockBegin() 0 21 11
B section() 0 19 9
A with() 0 8 4
B customHelper() 0 23 7
A doElse() 0 12 5
A compileLog() 0 6 1
B compileLookup() 0 13 9
B compileOutput() 0 9 9
A compileVariable() 0 10 3
A addUsageCount() 0 7 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
MIT License
5
Copyright 2013-2020 Zordius Chen. All Rights Reserved.
6
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:
7
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
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.
9
10
Origin: https://github.com/zordius/lightncandy
11
*/
12
13
/**
14
 * file of LightnCandy Compiler
15
 *
16
 * @package    LightnCandy
17
 * @author     Zordius <[email protected]>
18
 */
19
20
namespace LightnCandy;
21
22
/**
23
 * LightnCandy Compiler
24
 */
25
class Compiler extends Validator
26
{
27
    public static $lastParsed;
28
29
    /**
30
     * Compile template into PHP code
31
     *
32
     * @param array<string,array|string|integer> $context Current context
33
     * @param string $template handlebars template
34
     *
35
     * @return string|null generated PHP code
36
     */
37 795
    public static function compileTemplate(&$context, $template)
38
    {
39 795
        array_unshift($context['parsed'], array());
40 795
        Validator::verify($context, $template);
41 795
        static::$lastParsed = $context['parsed'];
42
43 795
        if (count($context['error'])) {
44 78
            return;
45
        }
46
47 718
        Parser::setDelimiter($context);
48
49 718
        $context['compile'] = true;
50
51
        // Handle dynamic partials
52 718
        Partial::handleDynamic($context);
53
54
        // Do PHP code generation.
55 718
        $code = '';
56 718
        foreach ($context['parsed'][0] as $info) {
57 718
            if (is_array($info)) {
58 677
                $context['tokens']['current']++;
59 677
                $code .= "'" . static::compileToken($context, $info) . "'";
60
            } else {
61 504
                $code .= $info;
62
            }
63
        }
64
65 718
        array_shift($context['parsed']);
66
67 718
        return $code;
68
    }
69
70
    /**
71
     * Compose LightnCandy render codes for include()
72
     *
73
     * @param array<string,array|string|integer> $context Current context
74
     * @param string $code generated PHP code
75
     *
76
     * @return string Composed PHP code
77
     */
78 717
    public static function composePHPRender($context, $code)
79
    {
80 717
        $flagJStrue = Expression::boolString($context['flags']['jstrue']);
81 717
        $flagJSObj = Expression::boolString($context['flags']['jsobj']);
82 717
        $flagJSLen = Expression::boolString($context['flags']['jslen']);
83 717
        $flagSPVar = Expression::boolString($context['flags']['spvar']);
84 717
        $flagProp = Expression::boolString($context['flags']['prop']);
85 717
        $flagMethod = Expression::boolString($context['flags']['method']);
86 717
        $flagLambda = Expression::boolString($context['flags']['lambda']);
87 717
        $flagMustlok = Expression::boolString($context['flags']['mustlok']);
88 717
        $flagMustlam = Expression::boolString($context['flags']['mustlam']);
89 717
        $flagMustsec = Expression::boolString($context['flags']['mustsec']);
90 717
        $flagEcho = Expression::boolString($context['flags']['echo']);
91 717
        $flagPartNC = Expression::boolString($context['flags']['partnc']);
92 717
        $flagKnownHlp = Expression::boolString($context['flags']['knohlp']);
93
94 717
        $constants = Exporter::constants($context);
95 717
        $helpers = Exporter::helpers($context);
96 717
        $partials = implode(",\n", $context['partialCode']);
97 717
        $debug = Runtime::DEBUG_ERROR_LOG;
98 717
        $use = $context['flags']['standalone'] ? Exporter::runtime($context) : "use {$context['runtime']} as {$context['runtimealias']};";
99 717
        $stringObject = $context['flags']['method'] || $context['flags']['prop'] ? Exporter::stringobject($context) : '';
100 717
        $safeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] === 0)) ? "use {$context['safestring']} as SafeString;" : '';
101 717
        $exportSafeString = (($context['usedFeature']['enc'] > 0) && ($context['flags']['standalone'] >0)) ? Exporter::safestring($context) : '';
102
        // Return generated PHP code string.
103
        return <<<VAREND
104 717
$stringObject{$safeString}{$use}{$exportSafeString}return function (\$in = null, \$options = null) {
105 717
    \$helpers = $helpers;
106 717
    \$partials = array($partials);
107
    \$cx = array(
108
        'flags' => array(
109 717
            'jstrue' => $flagJStrue,
110 717
            'jsobj' => $flagJSObj,
111 717
            'jslen' => $flagJSLen,
112 717
            'spvar' => $flagSPVar,
113 717
            'prop' => $flagProp,
114 717
            'method' => $flagMethod,
115 717
            'lambda' => $flagLambda,
116 717
            'mustlok' => $flagMustlok,
117 717
            'mustlam' => $flagMustlam,
118 717
            'mustsec' => $flagMustsec,
119 717
            'echo' => $flagEcho,
120 717
            'partnc' => $flagPartNC,
121 717
            'knohlp' => $flagKnownHlp,
122 717
            'debug' => isset(\$options['debug']) ? \$options['debug'] : $debug,
123
        ),
124 717
        'constants' => $constants,
125
        'helpers' => isset(\$options['helpers']) ? array_merge(\$helpers, \$options['helpers']) : \$helpers,
126
        'partials' => isset(\$options['partials']) ? array_merge(\$partials, \$options['partials']) : \$partials,
127
        'scopes' => array(),
128
        'sp_vars' => isset(\$options['data']) ? array_merge(array('root' => \$in), \$options['data']) : array('root' => \$in),
129
        'blparam' => array(),
130
        'partialid' => 0,
131 717
        'runtime' => '{$context['runtime']}',
132
    );
133 717
    {$context['renderex']}
134 717
    {$context['ops']['array_check']}
135 717
    {$context['ops']['op_start']}'$code'{$context['ops']['op_end']}
136
};
137
VAREND
138
        ;
139
    }
140
141
    /**
142
     * Get function name for standalone or none standalone template.
143
     *
144
     * @param array<string,array|string|integer> $context Current context of compiler progress.
145
     * @param string $name base function name
146
     * @param string $tag original handlabars tag for debug
147
     *
148
     * @return string compiled Function name
149
     *
150
     * @expect 'LR::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), 'test', ''
151
     * @expect 'LL::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 'LL'), 'test2', ''
152
     * @expect "lala_abctest3(" when input array('flags' => array('standalone' => 1, 'debug' => 0), 'runtime' => 'Runtime', 'runtimealias' => 0, 'funcprefix' => 'lala_abc'), 'test3', ''
153
     * @expect 'RR::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1), 'runtime' => 'Runtime', 'runtimealias' => 'RR', 'funcprefix' => 'haha456'), 'test', 'abc'
154
     */
155 649
    protected static function getFuncName(&$context, $name, $tag)
156
    {
157 649
        static::addUsageCount($context, 'runtime', $name);
158
159 649
        if ($context['flags']['debug'] && ($name != 'miss')) {
160 10
            $dbg = "'$tag', '$name', ";
161 10
            $name = 'debug';
162 10
            static::addUsageCount($context, 'runtime', 'debug');
163
        } else {
164 647
            $dbg = '';
165
        }
166
167 649
        return $context['flags']['standalone'] ? "{$context['funcprefix']}$name($dbg" : "{$context['runtimealias']}::$name($dbg";
168
    }
169
170
    /**
171
     * Get string presentation of variables
172
     *
173
     * @param array<string,array|string|integer> $context current compile context
174
     * @param array<array> $vn variable name array.
175
     * @param array<string>|null $blockParams block param list
176
     *
177
     * @return array<string|array> variable names
178
     *
179
     * @expect array('array(array($in),array())', array('this')) when input array('flags'=>array('spvar'=>true)), array(null)
180
     * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array('flags'=>array('spvar'=>true)), array(null, null)
181
     * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('flags'=>array('spvar'=>true)), array('a' => null)
182
     */
183 297
    protected static function getVariableNames(&$context, $vn, $blockParams = null)
184
    {
185 297
        $vars = array(array(), array());
186 297
        $exps = array();
187 297
        foreach ($vn as $i => $v) {
188 252
            $V = static::getVariableNameOrSubExpression($context, $v);
189 252
            if (is_string($i)) {
190 38
                $vars[1][] = "'$i'=>{$V[0]}";
191
            } else {
192 237
                $vars[0][] = $V[0];
193
            }
194 252
            $exps[] = $V[1];
195
        }
196 297
        $bp = $blockParams ? (',array(' . Expression::listString($blockParams) . ')') : '';
197 297
        return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . ")$bp)", $exps);
198
    }
199
200
    /**
201
     * Get string presentation of a sub expression
202
     *
203
     * @param array<string,array|string|integer> $context current compile context
204
     * @param array<boolean|integer|string|array> $vars parsed arguments list
205
     *
206
     * @return array<string> code representing passed expression
207
     */
208 46
    public static function compileSubExpression(&$context, $vars)
209
    {
210 46
        $ret = static::customHelper($context, $vars, true, true, true);
211
212 46
        if (($ret === null) && $context['flags']['lambda']) {
213 4
            $ret = static::compileVariable($context, $vars, true, true);
214
        }
215
216 46
        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
226
     */
227 418
    protected static function getVariableNameOrSubExpression(&$context, $var)
228
    {
229 418
        return Parser::isSubExp($var) ? static::compileSubExpression($context, $var[1]) : static::getVariableName($context, $var);
230
    }
231
232
    /**
233
     * Get string presentation of a variable
234
     *
235
     * @param array<array|string|integer> $var variable parsed path
236
     * @param array<array|string|integer> $context current compile context
237
     * @param array<string>|null $lookup extra lookup string as valid PHP variable name
238
     *
239
     * @return array<string> variable names
240
     *
241
     * @expect array('$in', 'this') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(null)
242
     * @expect array('(($inary && 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,'jslen'=>0)), array('true')
243
     * @expect array('(($inary && 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,'jslen'=>0)), array('false')
244
     * @expect array('true', 'true') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'true')
245
     * @expect array('false', 'false') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, 'false')
246
     * @expect array('(($inary && 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,'jslen'=>0)), array('2')
247
     * @expect array('2', '2') when input array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0)), array(-1, '2')
248
     * @expect array('(($inary && 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,'jslen'=>0)), array('@index')
249
     * @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,'jslen'=>0)), array('@index')
250
     * @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,'jslen'=>0)), array('@key')
251
     * @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,'jslen'=>0)), array('@first')
252
     * @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,'jslen'=>0)), array('@last')
253
     * @expect array('(($inary && 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,'jslen'=>0)), array('"a"')
254
     * @expect array('"a"', '"a"') when input array('flags'=>array('spvar'=>true,'debug'=>0)), array(-1, '"a"')
255
     * @expect array('(($inary && 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,'jslen'=>0)), array('a')
256
     * @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,'jslen'=>0)), array(1,'a')
257
     * @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,'jslen'=>0)), array(3,'a')
258
     * @expect array('(($inary && 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,'jslen'=>0)), array(null, 'id')
259
     * @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,'jslen'=>0,'standalone'=>0), 'runtime' => 'Runtime', 'runtimealias' => 'LR'), array(null, 'id')
260
     */
261 642
    protected static function getVariableName(&$context, $var, $lookup = null, $args = null)
262
    {
263 642
        if (isset($var[0]) && ($var[0] === Parser::LITERAL)) {
264 93
            if ($var[1] === "undefined") {
265 2
                $var[1] = "null";
266
            }
267 93
            return array($var[1], preg_replace('/\'(.*)\'/', '$1', $var[1]));
268
        }
269
270 599
        list($levels, $spvar, $var) = Expression::analyze($context, $var);
271 599
        $exp = Expression::toString($levels, $spvar, $var);
272 599
        $base = $spvar ? "\$cx['sp_vars']" : '$in';
273
274
        // change base when trace to parent
275 599
        if ($levels > 0) {
276 41
            if ($spvar) {
277 2
                $base .= str_repeat("['_parent']", $levels);
278
            } else {
279 39
                $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]";
280
            }
281
        }
282
283 599
        if ((empty($var) || (count($var) == 0) || (($var[0] === null) && (count($var) == 1))) && ($lookup === null)) {
284 167
            return array($base, $exp);
285
        }
286
287 527
        if ((count($var) > 0) && ($var[0] === null)) {
288 1
            array_shift($var);
289
        }
290
291
        // To support recursive context lookup, instance properties + methods and lambdas
292
        // the only way is using slower rendering time variable resolver.
293 527
        if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok'] || $context['flags']['mustlam'] || $context['flags']['lambda']) {
294 389
            $L = Expression::listString($var);
295 389
            $L = ($L === '') ? array() : array($L);
296 389
            if ($lookup) {
297 3
                $L[] = $lookup[0];
298
            }
299 389
            $A = $args ? ",$args[0]" : '';
300 389
            $E = $args ? ' ' . implode(' ', $args[1]) : '';
301 389
            return array(static::getFuncName($context, 'v', $exp) . "\$cx, \$in, isset($base) ? $base : null, array(" . implode(',', $L) . ")$A)", $lookup ? "lookup $exp $lookup[1]" : "$exp$E");
302
        }
303
304 139
        $n = Expression::arrayString($var);
305 139
        $k = array_pop($var);
306 139
        $L = $lookup ? "[{$lookup[0]}]" : '';
307 139
        $p = $lookup ? $n : (count($var) ? Expression::arrayString($var) : '');
308
309 139
        $checks = array();
310 139
        if ($levels > 0) {
311 11
            $checks[] = "isset($base)";
312
        }
313 139
        if (!$spvar) {
314 135
            if (($levels === 0) && $p) {
315 15
                $checks[] = "isset($base$p)";
316
            }
317 135
            $checks[] = ("$base$p" == '$in') ? '$inary' : "is_array($base$p)";
318
        }
319 139
        $checks[] = "isset($base$n$L)";
320 139
        $check = ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '');
321
322 139
        $lenStart = '';
323 139
        $lenEnd = '';
324
325 139
        if ($context['flags']['jslen']) {
326 54
            if (($lookup === null) && ($k === 'length')) {
327 1
                array_pop($checks);
328 1
                $lenStart = '(' . ((count($checks) > 1) ? '(' : '') . implode(' && ', $checks) . ((count($checks) > 1) ? ')' : '') . " ? count($base" . Expression::arrayString($var) . ') : ';
329 1
                $lenEnd = ')';
330
            }
331
        }
332
333 139
        return array("($check ? $base$n$L : $lenStart" . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null') . ")$lenEnd", $lookup ? "lookup $exp $lookup[1]" : $exp);
334
    }
335
336
    /**
337
     * Return compiled PHP code for a handlebars token
338
     *
339
     * @param array<string,array|string|integer> $context current compile context
340
     * @param array<string,array|boolean> $info parsed information
341
     *
342
     * @return string Return compiled code segment for the token
343
     */
344 677
    protected static function compileToken(&$context, $info)
345
    {
346 677
        list($raw, $vars, $token, $indent) = $info;
347
348 677
        $context['tokens']['partialind'] = $indent;
349 677
        $context['currentToken'] = $token;
350
351 677
        if ($ret = static::operator($token[Token::POS_OP], $context, $vars)) {
352 424
            return $ret;
353
        }
354
355 518
        if (isset($vars[0][0])) {
356 484
            if ($ret = static::customHelper($context, $vars, $raw, true)) {
357 122
                return static::compileOutput($context, $ret, 'FIXME: helper', $raw, false);
358
            }
359 370
            if ($context['flags']['else'] && ($vars[0][0] === 'else')) {
360 37
                return static::doElse($context, $vars);
361
            }
362 344
            if ($vars[0][0] === 'lookup') {
363 5
                return static::compileLookup($context, $vars, $raw);
364
            }
365 339
            if ($vars[0][0] === 'log') {
366 2
                return static::compileLog($context, $vars, $raw);
367
            }
368
        }
369
370 379
        return static::compileVariable($context, $vars, $raw, false);
371
    }
372
373
    /**
374
     * handle partial
375
     *
376
     * @param array<string,array|string|integer> $context current compile context
377
     * @param array<boolean|integer|string|array> $vars parsed arguments list
378
     *
379
     * @return string Return compiled code segment for the partial
380
     */
381 110
    public static function partial(&$context, $vars)
382
    {
383 110
        Parser::getBlockParams($vars);
384 110
        $pid = Parser::getPartialBlock($vars);
385 110
        $p = array_shift($vars);
386 110
        if ($context['flags']['runpart']) {
387 100
            if (!isset($vars[0])) {
388 93
                $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
389
            }
390 100
            $v = static::getVariableNames($context, $vars);
391 100
            $tag = ">$p[0] " .implode(' ', $v[1]);
392 100
            if (Parser::isSubExp($p)) {
393 6
                list($p) = static::compileSubExpression($context, $p[1]);
394
            } else {
395 94
                $p = "'$p[0]'";
396
            }
397 100
            $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : '';
398 100
            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...
399
        }
400 10
        return isset($context['usedPartial'][$p[0]]) ? "{$context['ops']['seperator']}'" . Partial::compileStatic($context, $p[0]) . "'{$context['ops']['seperator']}" : $context['ops']['seperator'];
401
    }
402
403
    /**
404
     * handle inline partial
405
     *
406
     * @param array<string,array|string|integer> $context current compile context
407
     * @param array<boolean|integer|string|array> $vars parsed arguments list
408
     *
409
     * @return string Return compiled code segment for the partial
410
     */
411 13
    public static function inline(&$context, $vars)
412
    {
413 13
        Parser::getBlockParams($vars);
414 13
        list($code) = array_shift($vars);
415 13
        $p = array_shift($vars);
416 13
        if (!isset($vars[0])) {
417 13
            $vars[0] = $context['flags']['partnc'] ? array(0, 'null') : array();
418
        }
419 13
        $v = static::getVariableNames($context, $vars);
420 13
        $tag = ">*inline $p[0]" .implode(' ', $v[1]);
421 13
        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...
422
    }
423
424
    /**
425
     * Return compiled PHP code for a handlebars inverted section begin token
426
     *
427
     * @param array<string,array|string|integer> $context current compile context
428
     * @param array<boolean|integer|string|array> $vars parsed arguments list
429
     *
430
     * @return string Return compiled code segment for the token
431
     */
432 38
    protected static function invertedSection(&$context, $vars)
433
    {
434 38
        $v = static::getVariableName($context, $vars[0]);
435 38
        return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}";
436
    }
437
438
    /**
439
     * Return compiled PHP code for a handlebars block custom helper begin token
440
     *
441
     * @param array<string,array|string|integer> $context current compile context
442
     * @param array<boolean|integer|string|array> $vars parsed arguments list
443
     * @param boolean $inverted the logic will be inverted
444
     *
445
     * @return string Return compiled code segment for the token
446
     */
447 64
    protected static function blockCustomHelper(&$context, $vars, $inverted = false)
448
    {
449 64
        $bp = Parser::getBlockParams($vars);
450 64
        $ch = array_shift($vars);
451 64
        $inverted = $inverted ? 'true' : 'false';
452 64
        static::addUsageCount($context, 'helpers', $ch[0]);
453 64
        $v = static::getVariableNames($context, $vars, $bp);
454
455 64
        return $context['ops']['seperator'] . static::getFuncName($context, 'hbbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['array_check']}{$context['ops']['f_start']}";
456
    }
457
458
    /**
459
     * Return compiled PHP code for a handlebars block end token
460
     *
461
     * @param array<string,array|string|integer> $context current compile context
462
     * @param array<boolean|integer|string|array> $vars parsed arguments list
463
     * @param string|null $matchop should also match to this operator
464
     *
465
     * @return string Return compiled code segment for the token
466
     */
467 346
    protected static function blockEnd(&$context, &$vars, $matchop = null)
468
    {
469 346
        $pop = $context['stack'][count($context['stack']) - 1];
470
471 346
        switch (isset($context['helpers'][$context['currentToken'][Token::POS_INNERTAG]]) ? 'skip' : $context['currentToken'][Token::POS_INNERTAG]) {
472 346
            case 'if':
473 287
            case 'unless':
474 85
                if ($pop === ':') {
475 32
                    array_pop($context['stack']);
476 32
                    return "{$context['ops']['cnd_end']}";
477
                }
478 57
                if (!$context['flags']['nohbh']) {
479 55
                    return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
480
                }
481 2
                break;
482 283
            case 'with':
483 33
                if (!$context['flags']['nohbh']) {
484 32
                    return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
485
                }
486
        }
487
488 260
        if ($pop === ':') {
489
            array_pop($context['stack']);
490
            return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
491
        }
492
493 260
        switch ($pop) {
494 260
            case '#':
495 235
                return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
496 33
            case '^':
497 33
                return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
498
        }
499
    }
500
501
    /**
502
     * Return compiled PHP code for a handlebars block begin token
503
     *
504
     * @param array<string,array|string|integer> $context current compile context
505
     * @param array<boolean|integer|string|array> $vars parsed arguments list
506
     *
507
     * @return string Return compiled code segment for the token
508
     */
509 261
    protected static function blockBegin(&$context, $vars)
510
    {
511 261
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
512 261
        if (!$context['flags']['nohbh']) {
513 222
            switch (isset($vars[0][0]) ? $vars[0][0] : null) {
514 222
                case 'if':
515 75
                    $includeZero = (isset($vars['includeZero'][1]) && $vars['includeZero'][1]) ? 'true' : 'false';
516 75
                    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...
517 166
                case 'unless':
518 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...
519 163
                case 'each':
520 54
                    return static::section($context, $vars, true);
0 ignored issues
show
Bug Compatibility introduced by
The expression static::section($context, $vars, true); of type string|null adds the type string to the return on line 520 which is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.
Loading history...
521 112
                case 'with':
522 32
                    if ($r = static::with($context, $vars)) {
523 32
                        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...
524
                    }
525
            }
526
        }
527
528 121
        return static::section($context, $vars);
0 ignored issues
show
Bug Compatibility introduced by
The expression static::section($context, $vars); of type string|null adds the type string to the return on line 528 which is incompatible with the return type of the parent method LightnCandy\Validator::blockBegin of type boolean.
Loading history...
529
    }
530
531
    /**
532
     * compile {{#foo}} token
533
     *
534
     * @param array<string,array|string|integer> $context current compile context
535
     * @param array<boolean|integer|string|array> $vars parsed arguments list
536
     * @param boolean $isEach the section is #each
537
     *
538
     * @return string|null Return compiled code segment for the token
539
     */
540 175
    protected static function section(&$context, $vars, $isEach = false)
541
    {
542 175
        $bs = 'null';
543 175
        $be = '';
544 175
        if ($isEach) {
545 54
            $bp = Parser::getBlockParams($vars);
546 54
            $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
547 54
            $be = $bp ? (' as |' . implode(' ', $bp) . '|') : '';
548 54
            array_shift($vars);
549
        }
550 175
        if ($context['flags']['lambda'] && !$isEach) {
551 69
            $V = array_shift($vars);
552 69
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
553
        } else {
554 106
            $v = static::getVariableNameOrSubExpression($context, $vars[0]);
555
        }
556 175
        $each = $isEach ? 'true' : 'false';
557 175
        return $context['ops']['seperator'] . static::getFuncName($context, 'sec', ($isEach ? 'each ' : '') . $v[1] . $be) . "\$cx, {$v[0]}, $bs, \$in, $each, function(\$cx, \$in) {{$context['ops']['array_check']}{$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...
558
    }
559
560
    /**
561
     * compile {{with}} token
562
     *
563
     * @param array<string,array|string|integer> $context current compile context
564
     * @param array<boolean|integer|string|array> $vars parsed arguments list
565
     *
566
     * @return string|null Return compiled code segment for the token
567
     */
568 32
    protected static function with(&$context, $vars)
569
    {
570 32
        $v = isset($vars[1]) ? static::getVariableNameOrSubExpression($context, $vars[1]) : array(null, array());
571 32
        $bp = Parser::getBlockParams($vars);
572 32
        $bs = $bp ? ('array(' . Expression::listString($bp) . ')') : 'null';
573 32
        $be = $bp ? " as |$bp[0]|" : '';
574 32
        return $context['ops']['seperator'] . static::getFuncName($context, 'wi', 'with ' . $v[1] . $be) . "\$cx, {$v[0]}, $bs, \$in, function(\$cx, \$in) {{$context['ops']['array_check']}{$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...
575
    }
576
577
    /**
578
     * Return compiled PHP code for a handlebars custom helper 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
     * @param boolean $raw is this {{{ token or not
583
     * @param boolean $nosep true to compile without seperator
584
     * @param boolean $subExp true when compile for subexpression
585
     *
586
     * @return string|null Return compiled code segment for the token when the token is custom helper
587
     */
588 494
    protected static function customHelper(&$context, $vars, $raw, $nosep, $subExp = false)
589
    {
590 494
        if (count($vars[0]) > 1) {
591 72
            return;
592
        }
593
594 442
        if (!isset($context['helpers'][$vars[0][0]])) {
595 321
            if ($subExp) {
596 7
                if ($vars[0][0] == 'lookup') {
597 3
                    return static::compileLookup($context, $vars, $raw, true);
598
                }
599
            }
600 318
            return;
601
        }
602
603 133
        $fn = $raw ? 'raw' : $context['ops']['enc'];
604 133
        $ch = array_shift($vars);
605 133
        $v = static::getVariableNames($context, $vars);
606 133
        static::addUsageCount($context, 'helpers', $ch[0]);
607 133
        $sep = $nosep ? '' : $context['ops']['seperator'];
608
609 133
        return $sep . static::getFuncName($context, 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn', \$in)$sep";
610
    }
611
612
    /**
613
     * Return compiled PHP code for a handlebars else token
614
     *
615
     * @param array<string,array|string|integer> $context current compile context
616
     * @param array<boolean|integer|string|array> $vars parsed arguments list
617
     *
618
     * @return string Return compiled code segment for the token when the token is else
619
     */
620 59
    protected static function doElse(&$context, $vars)
621
    {
622 59
        $v = $context['stack'][count($context['stack']) - 2];
623
624 59
        if ((($v === '[if]') && !isset($context['helpers']['if'])) ||
625 59
           (($v === '[unless]') && !isset($context['helpers']['unless']))) {
626 32
            $context['stack'][] = ':';
627 32
            return "{$context['ops']['cnd_else']}";
628
        }
629
630 30
        return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['array_check']}{$context['ops']['f_start']}";
631
    }
632
633
    /**
634
     * Return compiled PHP code for a handlebars log token
635
     *
636
     * @param array<string,array|string|integer> $context current compile context
637
     * @param array<boolean|integer|string|array> $vars parsed arguments list
638
     * @param boolean $raw is this {{{ token or not
639
     *
640
     * @return string Return compiled code segment for the token
641
     */
642 2
    protected static function compileLog(&$context, &$vars, $raw)
643
    {
644 2
        array_shift($vars);
645 2
        $v = static::getVariableNames($context, $vars);
646 2
        return $context['ops']['seperator'] . static::getFuncName($context, 'lo', $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}";
647
    }
648
649
    /**
650
     * Return compiled PHP code for a handlebars lookup token
651
     *
652
     * @param array<string,array|string|integer> $context current compile context
653
     * @param array<boolean|integer|string|array> $vars parsed arguments list
654
     * @param boolean $raw is this {{{ token or not
655
     * @param boolean $nosep true to compile without seperator
656
     *
657
     * @return string Return compiled code segment for the token
658
     */
659 8
    protected static function compileLookup(&$context, &$vars, $raw, $nosep = false)
660
    {
661 8
        $v2 = static::getVariableName($context, $vars[2]);
662 8
        $v = static::getVariableName($context, $vars[1], $v2);
663 8
        $sep = $nosep ? '' : $context['ops']['seperator'];
664 8
        $ex = $nosep ? ', 1' : '';
665
666 8
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) {
667 7
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}$ex){$sep}";
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...
668
        } else {
669 1
            return $raw ? "{$sep}$v[0]{$sep}" : "{$sep}htmlspecialchars((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$sep}";
670
        }
671
    }
672
673
    /**
674
     * Return compiled PHP code for template output
675
     *
676
     * @param array<string,array|string|integer> $context current compile context
677
     * @param string $variable PHP code for the variable
678
     * @param string $expression normalized handlebars expression
679
     * @param boolean $raw is this {{{ token or not
680
     * @param boolean $nosep true to compile without seperator
681
     *
682
     * @return string Return compiled code segment for the token
683
     */
684 491
    protected static function compileOutput(&$context, $variable, $expression, $raw, $nosep)
685
    {
686 491
        $sep = $nosep ? '' : $context['ops']['seperator'];
687 491
        if ($context['flags']['hbesc'] || $context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug'] || $nosep) {
688 441
            return $sep . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $expression) . "\$cx, $variable)$sep";
689
        } else {
690 50
            return $raw ? "$sep$variable{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlspecialchars((string)$variable, ENT_QUOTES, 'UTF-8')$sep";
691
        }
692
    }
693
694
    /**
695
     * Return compiled PHP code for a handlebars variable token
696
     *
697
     * @param array<string,array|string|integer> $context current compile context
698
     * @param array<boolean|integer|string|array> $vars parsed arguments list
699
     * @param boolean $raw is this {{{ token or not
700
     * @param boolean $nosep true to compile without seperator
701
     *
702
     * @return string Return compiled code segment for the token
703
     */
704 382
    protected static function compileVariable(&$context, &$vars, $raw, $nosep)
705
    {
706 382
        if ($context['flags']['lambda']) {
707 226
            $V = array_shift($vars);
708 226
            $v = static::getVariableName($context, $V, null, count($vars) ? static::getVariableNames($context, $vars) : array('0',array('')));
709
        } else {
710 156
            $v = static::getVariableName($context, $vars[0]);
711
        }
712 382
        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...
713
    }
714
715
    /**
716
     * Add usage count to context
717
     *
718
     * @param array<string,array|string|integer> $context current context
719
     * @param string $category category name, can be one of: 'var', 'helpers', 'runtime'
720
     * @param string $name used name
721
     * @param integer $count increment
722
     *
723
     * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname'
724
     * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname'
725
     * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3
726
     */
727 649
    protected static function addUsageCount(&$context, $category, $name, $count = 1)
728
    {
729 649
        if (!isset($context['usedCount'][$category][$name])) {
730 649
            $context['usedCount'][$category][$name] = 0;
731
        }
732 649
        return ($context['usedCount'][$category][$name] += $count);
733
    }
734
}
735