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