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 ( 5b9acd...8e76f6 )
by Zordius
04:37
created

Runtime::wi()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 8.8571
cc 5
eloc 9
nc 6
nop 6
crap 5
1
<?php
2
/*
3
4
Copyrights for code authored by Yahoo! Inc. is licensed under the following terms:
5
MIT License
6
Copyright (c) 2013-2015 Yahoo! Inc. All Rights Reserved.
7
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
11
Origin: https://github.com/zordius/lightncandy
12
*/
13
14
/**
15
 * file to support LightnCandy compiled PHP runtime
16
 *
17
 * @package    LightnCandy
18
 * @author     Zordius <[email protected]>
19
 */
20
21
namespace LightnCandy;
22
23
/**
24
 * LightnCandy class for compiled PHP runtime.
25
 */
26
class Runtime
27
{
28
    const DEBUG_ERROR_LOG = 1;
29
    const DEBUG_ERROR_EXCEPTION = 2;
30
    const DEBUG_TAGS = 4;
31
    const DEBUG_TAGS_ANSI = 12;
32
    const DEBUG_TAGS_HTML = 20;
33
34
    /**
35
     * LightnCandy runtime method for output debug info.
36
     *
37
     * @param string $v expression
38
     * @param string $f runtime function name
39
     * @param array<string,array|string|integer> $cx render time context
40
     *
41
     * @expect '{{123}}' when input '123', 'miss', array('flags' => array('debug' => Runtime::DEBUG_TAGS), 'runtime' => 'LightnCandy\\Runtime'), ''
42
     * @expect '<!--MISSED((-->{{#123}}<!--))--><!--SKIPPED--><!--MISSED((-->{{/123}}<!--))-->' when input '123', 'wi', array('flags' => array('debug' => Runtime::DEBUG_TAGS_HTML), 'runtime' => 'LightnCandy\\Runtime'), false, null, false, function () {return 'A';}
43
     */
44 9
    public static function debug($v, $f, $cx) {
45 9
        $params = array_slice(func_get_args(), 2);
46 9
        $r = call_user_func_array((isset($cx['funcs'][$f]) ? $cx['funcs'][$f] : "{$cx['runtime']}::$f"), $params);
47
48 8
        if ($cx['flags']['debug'] & static::DEBUG_TAGS) {
49 5
            $ansi = $cx['flags']['debug'] & (static::DEBUG_TAGS_ANSI - static::DEBUG_TAGS);
50 5
            $html = $cx['flags']['debug'] & (static::DEBUG_TAGS_HTML - static::DEBUG_TAGS);
51 5
            $cs = ($html ? (($r !== '') ? '<!!--OK((-->' : '<!--MISSED((-->') : '')
52 5
                  . ($ansi ? (($r !== '') ? "\033[0;32m" : "\033[0;31m") : '');
53 5
            $ce = ($html ? '<!--))-->' : '')
54 5
                  . ($ansi ? "\033[0m" : '');
55
            switch ($f) {
56 5
                case 'sec':
57 3
                case 'wi':
58 3
                    if ($r == '') {
59 3
                        if ($ansi) {
60 1
                            $r = "\033[0;33mSKIPPED\033[0m";
61
                        }
62 3
                        if ($html) {
63 2
                            $r = '<!--SKIPPED-->';
64
                        }
65
                    }
66 3
                    return "$cs{{#{$v}}}$ce{$r}$cs{{/{$v}}}$ce";
67
                default:
68 3
                    return "$cs{{{$v}}}$ce";
69
            }
70
        } else {
71 3
            return $r;
72
        }
73
    }
74
75
    /**
76
     * LightnCandy runtime method for error
77
     *
78
     * @param array<string,array|string|integer> $cx render time context
79
     * @param string $err error message
80
     *
81
     * @throws \Exception
82
     */
83 11
    public static function err($cx, $err) {
84 11
        if ($cx['flags']['debug'] & static::DEBUG_ERROR_LOG) {
85 2
            error_log($err);
86 2
            return;
87
        }
88 9
        if ($cx['flags']['debug'] & static::DEBUG_ERROR_EXCEPTION) {
89 6
            throw new \Exception($err);
90
        }
91 3
    }
92
93
    /**
94
     * LightnCandy runtime method for missing data error.
95
     *
96
     * @param array<string,array|string|integer> $cx render time context
97
     * @param string $v expression
98
     */
99 5
    public static function miss($cx, $v) {
100 5
        static::err($cx, "Runtime: $v is not exist");
101 4
    }
102
103
    /**
104
     * LightnCandy runtime method for log.
105
     *
106
     * @param array<string,array|string|integer> $cx render time context
107
     * @param string $v expression
108
     */
109
    public static function lo($cx, $v) {
0 ignored issues
show
Unused Code introduced by
The parameter $cx is not used and could be removed.

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

Loading history...
110
        error_log(var_export($v[0], true));
111
        return '';
112
    }
113
114
    /**
115
     * LightnCandy runtime method for variable lookup. It is slower and only be used for instance property or method detection or lambdas.
116
     *
117
     * @param array<string,array|string|integer> $cx render time context
118
     * @param array|string|boolean|integer|double|null $in current context
119
     * @param array<array|string|integer> $base current variable context
120
     * @param array<string|integer> $path array of names for path
121
     * @param array|null $args extra arguments for lambda
122
     *
123
     * @return null|string Return the value or null when not found
124
     *
125
     * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), null, 0, array('a', 'b')
126
     * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), null, array('a' => array('b' => 3)), array('a', 'b')
127
     * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), null, (Object) array('a' => array('b' => 3)), array('a', 'b')
128
     * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 1, 'method' => 0, 'mustlok' => 0)), null, (Object) array('a' => array('b' => 3)), array('a', 'b')
129
     */
130 385
    public static function v($cx, $in, $base, $path, $args = null) {
131 385
        $count = count($cx['scopes']);
132 385
        while ($base) {
133 349
            $v = $base;
134 349
            foreach ($path as $name) {
135 349
                if (is_array($v) && isset($v[$name])) {
136 344
                    $v = $v[$name];
137 344
                    continue;
138
                }
139 36
                if (is_object($v)) {
140 5
                    if ($cx['flags']['prop'] && !($v instanceof \Closure) && isset($v->$name)) {
141 3
                        $v = $v->$name;
142 3
                        continue;
143
                    }
144 5
                    if ($cx['flags']['method'] && is_callable(array($v, $name))) {
145 4
                        $v = $v->$name();
146 4
                        continue;
147
                    }
148
                }
149 34
                if ($cx['flags']['mustlok']) {
150 31
                    unset($v);
151 31
                    break;
152
                }
153 3
                return null;
154
            }
155 349
            if (isset($v)) {
156 338
                if ($v instanceof \Closure) {
157 30
                    if ($cx['flags']['mustlam'] || $cx['flags']['lambda']) {
158 30
                        if (!$cx['flags']['knohlp'] && ($args || ($args === 0))) {
159 20
                            $A = $args ? $args[0] : array();
160 20
                            $A[] = array('hash' => $args[1], '_this' => $in);
161
                        } else {
162 11
                            $A = array($in);
163
                        }
164 30
                        $v = call_user_func_array($v, $A);
165
                    }
166
                }
167 338
                return $v;
168
            }
169 31
            $count--;
170
            switch ($count) {
171 31
                case -1:
172 30
                    $base = $cx['sp_vars']['root'];
173 30
                    break;
174 6
                case -2:
175 29
                    return null;
176
                default:
177 6
                    $base = $cx['scopes'][$count];
178
            }
179
        }
180 40
        if ($args) {
181 3
            static::err($cx, 'Can not find helper or lambda: "' . implode('.', $path) . '" !');
182
        }
183 40
    }
184
185
    /**
186
     * LightnCandy runtime method for {{#if var}}.
187
     *
188
     * @param array<string,array|string|integer> $cx render time context
189
     * @param array<array|string|integer>|string|integer|null $v value to be tested
190
     * @param boolean $zero include zero as true
191
     *
192
     * @return boolean Return true when the value is not null nor false.
193
     *
194
     * @expect false when input array(), null, false
195
     * @expect false when input array(), 0, false
196
     * @expect true when input array(), 0, true
197
     * @expect false when input array(), false, false
198
     * @expect true when input array(), true, false
199
     * @expect true when input array(), 1, false
200
     * @expect false when input array(), '', false
201
     * @expect false when input array(), array(), false
202
     * @expect true when input array(), array(''), false
203
     * @expect true when input array(), array(0), false
204
     */
205 64
    public static function ifvar($cx, $v, $zero) {
0 ignored issues
show
Unused Code introduced by
The parameter $cx is not used and could be removed.

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

Loading history...
206 64
        return ($v !== null) && ($v !== false) && ($zero || ($v !== 0) && ($v !== 0.0)) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
207
    }
208
209
    /**
210
     * LightnCandy runtime method for {{^var}} inverted section.
211
     *
212
     * @param array<string,array|string|integer> $cx render time context
213
     * @param array<array|string|integer>|string|integer|null $v value to be tested
214
     *
215
     * @return boolean Return true when the value is not null nor false.
216
     *
217
     * @expect true when input array(), null
218
     * @expect false when input array(), 0
219
     * @expect true when input array(), false
220
     * @expect false when input array(), 'false'
221
     * @expect true when input array(), array()
222
     * @expect false when input array(), array('1')
223
     */
224 37
    public static function isec($cx, $v) {
0 ignored issues
show
Unused Code introduced by
The parameter $cx is not used and could be removed.

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

Loading history...
225 37
        return ($v === null) || ($v === false) || (is_array($v) && (count($v) === 0));
226
    }
227
228
    /**
229
     * LightnCandy runtime method for {{{var}}} .
230
     *
231
     * @param array<string,array|string|integer> $cx render time context
232
     * @param array<array|string|integer>|string|integer|null $v value to be output
233
     *
234
     * @return string The raw value of the specified variable
235
     *
236
     * @expect true when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), true
237
     * @expect 'true' when input array('flags' => array('jstrue' => 1)), true
238
     * @expect '' when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), false
239
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false
240
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true
241
     * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b')
242
     * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', 'b')
243
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b')
244
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b')
245
     * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', true)
246
     * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',true)
247
     * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
248
     * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
249
     */
250 313
    public static function raw($cx, $v) {
251 313
        if ($v === true) {
252 1
            if ($cx['flags']['jstrue']) {
253 1
                return 'true';
254
            }
255
        }
256
257 313
        if (($v === false)) {
258 6
            if ($cx['flags']['jstrue']) {
259 6
                return 'false';
260
            }
261
        }
262
263 308
        if (is_array($v)) {
264 6
            if ($cx['flags']['jsobj']) {
265 6
                if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) {
266 2
                    return '[object Object]';
267
                } else {
268 5
                    $ret = array();
269 5
                    foreach ($v as $k => $vv) {
270 5
                        $ret[] = static::raw($cx, $vv);
271
                    }
272 5
                    return join(',', $ret);
273
                }
274
            } else {
275 1
                return 'Array';
276
            }
277
        }
278
279 308
        return "$v";
280
    }
281
282
    /**
283
     * LightnCandy runtime method for {{var}} .
284
     *
285
     * @param array<string,array|string|integer> $cx render time context
286
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
287
     *
288
     * @return string The htmlencoded value of the specified variable
289
     *
290
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
291
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
292
     * @expect 'a&#039;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
293
     */
294 44
    public static function enc($cx, $var) {
295 44
        return htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8');
296
    }
297
298
    /**
299
     * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js .
300
     *
301
     * @param array<string,array|string|integer> $cx render time context
302
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
303
     *
304
     * @return string The htmlencoded value of the specified variable
305
     *
306
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
307
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
308
     * @expect 'a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
309
     * @expect '&#x60;a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), '`a\'b'
310
     */
311 240
    public static function encq($cx, $var) {
312 240
        return str_replace(array('=', '`', '&#039;'), array('&#x3D;', '&#x60;', '&#x27;'), htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8'));
313
    }
314
315
    /**
316
     * LightnCandy runtime method for {{#var}} section.
317
     *
318
     * @param array<string,array|string|integer> $cx render time context
319
     * @param array<array|string|integer>|string|integer|null $v value for the section
320
     * @param array<string>|null $bp block parameters
321
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
322
     * @param boolean $each true when rendering #each
323
     * @param Closure $cb callback function to render child context
324
     * @param Closure|null $else callback function to render child context when {{else}}
325
     *
326
     * @return string The rendered string of the section
327
     *
328
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, false, false, function () {return 'A';}
329
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), null, null, null, false, function () {return 'A';}
330
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), true, null, true, false, function () {return 'A';}
331
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function () {return 'A';}
332
     * @expect '-a=' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array('a'), null, array('a'), false, function ($c, $i) {return "-$i=";}
333
     * @expect '-a=-b=' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array('a','b'), null, array('a','b'), false, function ($c, $i) {return "-$i=";}
334
     * @expect '' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'abc', null, 'abc', true, function ($c, $i) {return "-$i=";}
335
     * @expect '-b=' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array('a' => 'b'), null, array('a' => 'b'), true, function ($c, $i) {return "-$i=";}
336
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'b', null, 'b', false, function ($c, $i) {return count($i);}
337
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 1, null, 1, false, function ($c, $i) {return print_r($i, true);}
338
     * @expect '0' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function ($c, $i) {return print_r($i, true);}
339
     * @expect '{"b":"c"}' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array('b' => 'c'), null, array('b' => 'c'), false, function ($c, $i) {return json_encode($i);}
340
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array(), null, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
341
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), array(), null, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
342
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
343
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
344
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), '', null, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
345
     * @expect 'cb' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), '', null, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
346
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
347
     * @expect 'cb' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
348
     * @expect 'inv' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), new stdClass, null, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
349
     * @expect 'cb' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), new stdClass, null, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
350
     * @expect '268' when input array('scopes' => array(), 'flags' => array('spvar' => 1, 'mustlam' => 0, 'lambda' => 0), 'sp_vars'=>array('root' => 0)), array(1,3,4), null, 0, false, function ($c, $i) {return $i * 2;}
351
     * @expect '038' when input array('scopes' => array(), 'flags' => array('spvar' => 1, 'mustlam' => 0, 'lambda' => 0), 'sp_vars'=>array('root' => 0)), array(1,3,'a'=>4), null, 0, true, function ($c, $i) {return $i * $c['sp_vars']['index'];}
352
     */
353 160
    public static function sec($cx, $v, $bp, $in, $each, $cb, $else = null) {
354 160
        $push = ($in !== $v) || $each;
355
356 160
        $isAry = is_array($v) || ($v instanceof \ArrayObject);
357 160
        $isTrav = $v instanceof \Traversable;
358 160
        $loop = $each;
359 160
        $keys = null;
360 160
        $last = null;
361 160
        $isObj = false;
362
363 160
        if ($isAry && $else !== null && count($v) === 0) {
364 3
            $ret = $else($cx, $in);
365 3
            return $ret;
366
        }
367
368
        // #var, detect input type is object or not
369 158
        if (!$loop && $isAry) {
370 65
            $keys = array_keys($v);
371 65
            $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
372 65
            $isObj = !$loop;
373
        }
374
375 158
        if ($cx['flags']['mustlam'] && ($v instanceof \Closure)) {
376
            static::err($cx, 'Do not support Section Lambdas!');
377
        }
378
379 158
        if (($loop && $isAry) || $isTrav) {
380 100
            if ($each && !$isTrav) {
381
                // Detect input type is object or not when never done once
382 45
                if ($keys == null) {
383 45
                    $keys = array_keys($v);
384 45
                    $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
385
                }
386
            }
387 100
            $ret = array();
388 100
            if ($push) {
389 98
                $cx['scopes'][] = $in;
390
            }
391 100
            $i = 0;
392 100
            if ($cx['flags']['spvar']) {
393 89
                $old_spvar = $cx['sp_vars'];
394 89
                $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $old_spvar, array('_parent' => $old_spvar));
395 89
                if (!$isTrav) {
396 88
                    $last = count($keys) - 1;
397
                }
398
            }
399
400 100
            $isSparceArray = $isObj && (count(array_filter(array_keys($v), 'is_string')) == 0);
401 100
            foreach ($v as $index => $raw) {
402 93
                if ($cx['flags']['spvar']) {
403 83
                    $cx['sp_vars']['first'] = ($i === 0);
404 83
                    $cx['sp_vars']['last'] = ($i == $last);
405 83
                    $cx['sp_vars']['key'] = $index;
406 83
                    $cx['sp_vars']['index'] = $isSparceArray ? $index : $i;
407 83
                    $i++;
408
                }
409 93
                if (isset($bp[0])) {
410 1
                    $raw = static::m($cx, $raw, array($bp[0] => $raw));
411
                }
412 93
                if (isset($bp[1])) {
413 1
                    $raw = static::m($cx, $raw, array($bp[1] => $cx['sp_vars']['index']));
414
                }
415 93
                $ret[] = $cb($cx, $raw);
416
            }
417 99
            if ($cx['flags']['spvar']) {
418 88
                if ($isObj) {
419 14
                    unset($cx['sp_vars']['key']);
420
                } else {
421 76
                    unset($cx['sp_vars']['last']);
422
                }
423 88
                unset($cx['sp_vars']['index']);
424 88
                unset($cx['sp_vars']['first']);
425 88
                $cx['sp_vars'] = $old_spvar;
0 ignored issues
show
Bug introduced by
The variable $old_spvar does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
426
            }
427 99
            if ($push) {
428 97
                array_pop($cx['scopes']);
429
            }
430 99
            return join('', $ret);
431
        }
432 61
        if ($each) {
433 2
            if ($else !== null) {
434 1
                $ret = $else($cx, $v);
435 1
                return $ret;
436
            }
437 2
            return '';
438
        }
439 60
        if ($isAry) {
440 11
            if ($push) {
441 10
                $cx['scopes'][] = $in;
442
            }
443 11
            $ret = $cb($cx, $v);
444 11
            if ($push) {
445 10
                array_pop($cx['scopes']);
446
            }
447 11
            return $ret;
448
        }
449
450 50
        if ($v === true) {
451 22
            return $cb($cx, $in);
452
        }
453
454 29
        if (($v !== null) && ($v !== false)) {
455 6
            return $cb($cx, $v);
456
        }
457
458 24
        if ($else !== null) {
459 10
            $ret = $else($cx, $in);
460 10
            return $ret;
461
        }
462
463 15
        return '';
464
    }
465
466
    /**
467
     * LightnCandy runtime method for {{#with var}} .
468
     *
469
     * @param array<string,array|string|integer> $cx render time context
470
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
471
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
472
     * @param array<string>|null $bp block parameters
473
     * @param Closure $cb callback function to render child context
474
     * @param Closure|null $else callback function to render child context when {{else}}
475
     *
476
     * @return string The rendered string of the token
477
     *
478
     * @expect '' when input array(), false, null, false, function () {return 'A';}
479
     * @expect '' when input array(), null, null, null, function () {return 'A';}
480
     * @expect '{"a":"b"}' when input array(), array('a'=>'b'), null, array('a'=>'c'), function ($c, $i) {return json_encode($i);}
481
     * @expect '-b=' when input array(), 'b', null, array('a'=>'b'), function ($c, $i) {return "-$i=";}
482
     */
483 25
    public static function wi($cx, $v, $bp, $in, $cb, $else = null) {
484 25
        if (isset($bp[0])) {
485 4
            $v = static::m($cx, $v, array($bp[0] => $v));
0 ignored issues
show
Documentation introduced by
array($bp[0] => $v) is of type array<string,array<integ...r>|string|integer|null>, but the function expects a array<integer,array|stri...er>|string|integer|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
486
        }
487 25
        if (($v === false) || ($v === null)) {
488 4
            return $else ? $else($cx, $in) : '';
489
        }
490 22
        $cx['scopes'][] = $in;
491 22
        $ret = $cb($cx, $v);
492 22
        array_pop($cx['scopes']);
493 22
        return $ret;
494
    }
495
496
    /**
497
     * LightnCandy runtime method to get merged context
498
     *
499
     * @param array<string,array|string|integer> $cx render time context
500
     * @param array<array|string|integer>|string|integer|null $a the context to be merged
501
     * @param array<array|string|integer>|string|integer|null $b the new context to overwrite
502
     *
503
     * @return array<array|string|integer>|string|integer the merged context object
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
504
     *
505
     */
506 82
    public static function m($cx, $a, $b) {
507 82
        if (is_array($b)) {
508 82
            if ($a === null) {
509 7
                return $b;
510 75
            } else if (is_array($a)) {
511 72
                return array_merge($a, $b);
512 3
            } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($a)) {
513 1
                foreach ($b as $i => $v) {
514 1
                    $a->$i = $v;
515
                }
516
            }
517
        }
518 3
        return $a;
519
    }
520
521
    /**
522
     * LightnCandy runtime method for {{> partial}} .
523
     *
524
     * @param array<string,array|string|integer> $cx render time context
525
     * @param string $p partial name
526
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
527
     *
528
     * @return string The rendered string of the partial
529
     *
530
     */
531 77
    public static function p($cx, $p, $v, $sp = '') {
532 77
        if (!isset($cx['partials'][$p])) {
533 1
            static::err($cx, "Can not find partial named as '$p' !!");
534
            return '';
535
        }
536
537 76
        return call_user_func($cx['partials'][$p], $cx, static::m($cx, $v[0][0], $v[1]), $sp);
538
    }
539
540
    /**
541
     * LightnCandy runtime method for inline partial.
542
     *
543
     * @param array<string,array|string|integer> $cx render time context
544
     * @param string $p partial name
545
     * @param Closure $code the compiled partial code
546
     *
547
     */
548 7
    public static function in(&$cx, $p, $code) {
549 7
        $cx['partials'][$p] = $code;
550 7
    }
551
552
    /**
553
     * LightnCandy runtime method for custom helpers.
554
     *
555
     * @param array<string,array|string|integer> $cx render time context
556
     * @param string $ch the name of custom helper to be executed
557
     * @param array<array> $vars variables for the helper
558
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
559
     *
560
     * @return string The rendered string of the token
561
     *
562
     * @expect '---' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('-'),array()), 'raw'
563
     * @expect '-&amp;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('&'),array()), 'enc'
564
     * @expect '-&#x27;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('\''),array()), 'encq'
565
     * @expect '-b-' when input array('helpers' => array('a' => function ($i,$j) {return "-{$j['a']}-";})), 'a', array(array(),array('a' => 'b')), 'raw'
566
     */
567 22
    public static function ch($cx, $ch, $vars, $op) {
568 22
        return static::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op);
569
    }
570
571
    /**
572
     * LightnCandy runtime method to handle response of custom helpers.
573
     *
574
     * @param string|array<string,array|string|integer> $ret return value from custom helper
575
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
576
     *
577
     * @return string The rendered string of the token
578
     *
579
     * @expect '-&-' when input '-&-', 'raw'
580
     * @expect '-&amp;&#039;-' when input '-&\'-', 'enc'
581
     * @expect '-&amp;&#x27;-' when input '-&\'-', 'encq'
582
     * @expect '-&amp;&#039;-' when input array('-&\'-'), 'enc'
583
     * @expect '-&amp;&#x27;-' when input array('-&\'-'), 'encq'
584
     * @expect '-&amp;-' when input array('-&-', false), 'enc'
585
     * @expect '-&-' when input array('-&-', false), 'raw'
586
     * @expect '-&-' when input array('-&-', 'raw'), 'enc'
587
     * @expect '-&amp;&#x27;-' when input array('-&\'-', 'encq'), 'raw'
588
     */
589 165
    public static function chret($ret, $op) {
590 165
        if (is_array($ret)) {
591 10
            if (isset($ret[1]) && $ret[1]) {
592 9
                $op = $ret[1];
593
            }
594 10
            $ret = $ret[0];
595
        }
596
597
        switch ($op) {
598 165
            case 'enc':
599 9
                return htmlentities($ret, ENT_QUOTES, 'UTF-8');
600 99
            case 'encq':
601 88
                return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities($ret, ENT_QUOTES, 'UTF-8'))));
602
        }
603 99
        return $ret;
604
    }
605
606
    /**
607
     * LightnCandy runtime method for Handlebars.js style custom helpers.
608
     *
609
     * @param array<string,array|string|integer> $cx render time context
610
     * @param string $ch the name of custom helper to be executed
611
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
612
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
613
     * @param boolean $inverted the logic will be inverted
614
     * @param Closure|null $cb callback function to render child context
615
     * @param Closure|null $else callback function to render child context when {{else}}
616
     *
617
     * @return string The rendered string of the token
618
     */
619 145
    public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
620 145
        $isBlock = (is_object($cb) && ($cb instanceof \Closure));
621
622 145
        if (isset($cx['blparam'][0][$ch])) {
623 1
            return $cx['blparam'][0][$ch];
624
        }
625
626 145
        $args = $vars[0];
627
        $options = array(
628 145
            'name' => $ch,
629 145
            'hash' => $vars[1],
630 145
            'contexts' => count($cx['scopes']) ? $cx['scopes'] : array(null),
631 145
            'fn.blockParams' => 0,
632
        );
633
634 145
        if ($isBlock) {
635 58
            $options['_this'] = &$op;
636
        } else {
637 97
            $options['_this'] = &$inverted;
638
        }
639
640 145
        if (isset($vars[2])) {
641 3
            $options['fn.blockParams'] = count($vars[2]);
642
        }
643
644
        // $invert the logic
645 145
        if ($inverted) {
646 63
            $tmp = $else;
647 63
            $else = $cb;
648 63
            $cb = $tmp;
649
        }
650
651 145
        if ($isBlock) {
652
            $options['fn'] = function ($context = '_NO_INPUT_HERE_', $data = null) use ($cx, &$op, $cb, $options, $vars) {
653 46
                if ($cx['flags']['echo']) {
654 6
                    ob_start();
655
                }
656 46
                if (isset($data['data'])) {
657 6
                    $old_spvar = $cx['sp_vars'];
658 6
                    $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $data['data'], array('_parent' => $old_spvar));
659
                }
660 46
                $ex = false;
661 46
                if (isset($data['blockParams']) && isset($vars[2])) {
662 3
                    $ex = array_combine($vars[2], array_slice($data['blockParams'], 0, count($vars[2])));
663 3
                    array_unshift($cx['blparam'], $ex);
664 44
                } else if (isset($cx['blparam'][0])) {
665 1
                    $ex = $cx['blparam'][0];
666
                }
667 46
                if (($context === '_NO_INPUT_HERE_') || ($context === $op)) {
668 27
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $op, $ex) : $op);
669
                } else {
670 22
                    $cx['scopes'][] = $op;
671 22
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $context, $ex) : $context);
672 22
                    array_pop($cx['scopes']);
673
                }
674 46
                if (isset($data['data'])) {
675 6
                    $cx['sp_vars'] = $old_spvar;
0 ignored issues
show
Bug introduced by
The variable $old_spvar does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
676
                }
677 46
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
678
            };
679
        }
680
681 145
        if ($else) {
682 7
            $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
683 7
                if ($cx['flags']['echo']) {
684 3
                    ob_start();
685
                }
686 7
                if ($context === '_NO_INPUT_HERE_') {
687 2
                    $ret = $else($cx, $op);
688
                } else {
689 5
                    $cx['scopes'][] = $op;
690 5
                    $ret = $else($cx, $context);
691 5
                    array_pop($cx['scopes']);
692
                }
693 7
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
694
            };
695
        }
696
697 145
        if ($cx['flags']['spvar']) {
698 134
            $options['data'] = $cx['sp_vars'];
699
        }
700
701 145
        $args[] = $options;
702 145
        $e = null;
703 145
        $r = true;
704
705
        try {
706 145
            $r = call_user_func_array($cx['hbhelpers'][$ch], $args);
707 2
        } catch (\Exception $E) {
708 2
            $e = "Runtime: call custom helper '$ch' error: " . $E->getMessage();
709
        }
710
711 145
        if($e !== null) {
712 2
            static::err($cx, $e);
713
        }
714
715 144
        return static::chret($r, $isBlock ? 'raw' : $op);
716
    }
717
718
    /**
719
     * LightnCandy runtime method for block custom helpers.
720
     *
721
     * @param array<string,array|string|integer> $cx render time context
722
     * @param string $ch the name of custom helper to be executed
723
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
724
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
725
     * @param boolean $inverted the logic will be inverted
726
     * @param Closure $cb callback function to render child context
727
     * @param Closure|null $else callback function to render child context when {{else}}
728
     *
729
     * @return string The rendered string of the token
730
     *
731
     * @expect '4.2.3' when input array('blockhelpers' => array('a' => function ($cx) {return array($cx,2,3);})), 'a', array(0, 0), 4, false, function($cx, $i) {return implode('.', $i);}
732
     * @expect '2.6.5' when input array('blockhelpers' => array('a' => function ($cx,$in) {return array($cx,$in[0],5);})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
733
     * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
734
     */
735 4
    public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = null) {
736 4
        $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]);
737
738
        // $invert the logic
739 4
        if ($inverted) {
740 2
            $tmp = $else;
741 2
            $else = $cb;
742 2
            $cb = $tmp;
743
        }
744
745 4
        $ret = '';
746 4
        if ($r === null) {
747 2
            if ($else) {
748 1
                $cx['scopes'][] = $in;
749 1
                $ret = $else($cx, $r);
750 2
                array_pop($cx['scopes']);
751
            }
752
        } else {
753 3
            if ($cb) {
754 3
                $cx['scopes'][] = $in;
755 3
                $ret = $cb($cx, $r);
756 3
                array_pop($cx['scopes']);
757
            }
758
        }
759
760 4
        return $ret;
761
    }
762
}
763
764