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 ( 876585...046de7 )
by Zordius
02:33
created

src/Compiler.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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