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 — v0.89-develop ( 2a64a1...dbf016 )
by Zordius
03:45
created

Runtime::chret()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6
Metric Value
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 8.8571
cc 6
eloc 11
nc 9
nop 2
crap 6
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 8
    public static function err($cx, $err) {
84 8
        if ($cx['flags']['debug'] & static::DEBUG_ERROR_LOG) {
85 2
            error_log($err);
86 2
            return;
87
        }
88 6
        if ($cx['flags']['debug'] & static::DEBUG_ERROR_EXCEPTION) {
89 3
            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 variable lookup. It is slower and only be used for instance property or method detection or lambdas.
105
     *
106
     * @param array<string,array|string|integer> $cx render time context
107
     * @param array|string|boolean|integer|double|null $in current context
108
     * @param array<array|string|integer> $base current variable context
109
     * @param array<string|integer> $path array of names for path
110
     *
111
     * @return null|string Return the value or null when not found
112
     *
113
     * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), null, 0, array('a', 'b')
114
     * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), null, array('a' => array('b' => 3)), array('a', 'b')
115
     * @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')
116
     * @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')
117
     */
118 355
    public static function v($cx, $in, $base, $path) {
119 355
        $count = count($cx['scopes']);
120 355
        while ($base) {
121 324
            $v = $base;
122 324
            foreach ($path as $name) {
123 324
                if (is_array($v) && isset($v[$name])) {
124 319
                    $v = $v[$name];
125 319
                    continue;
126
                }
127 32
                if (is_object($v)) {
128 5
                    if ($cx['flags']['prop'] && !($v instanceof \Closure) && isset($v->$name)) {
129 3
                        $v = $v->$name;
130 3
                        continue;
131
                    }
132 5
                    if ($cx['flags']['method'] && is_callable(array($v, $name))) {
133 4
                        $v = $v->$name();
134 4
                        continue;
135
                    }
136
                }
137 30
                if ($cx['flags']['mustlok']) {
138 27
                    unset($v);
139 27
                    break;
140
                }
141 3
                return null;
142
            }
143 324
            if (isset($v)) {
144 313
                if ($v instanceof \Closure) {
145 20
                    if ($cx['flags']['mustlam'] || $cx['flags']['lambda']) {
146 20
                        $v = $v($in);
147
                    }
148
                }
149 313
                return $v;
150
            }
151 27
            $count--;
152
            switch ($count) {
153 27
                case -1:
154 26
                    $base = $cx['sp_vars']['root'];
155 26
                    break;
156 5
                case -2:
157 25
                    return null;
158
                default:
159 5
                    $base = $cx['scopes'][$count];
160
            }
161
        }
162 35
    }
163
164
    /**
165
     * LightnCandy runtime method for {{#if var}}.
166
     *
167
     * @param array<string,array|string|integer> $cx render time context
168
     * @param array<array|string|integer>|string|integer|null $v value to be tested
169
     * @param boolean $zero include zero as true
170
     *
171
     * @return boolean Return true when the value is not null nor false.
172
     *
173
     * @expect false when input array(), null, false
174
     * @expect false when input array(), 0, false
175
     * @expect true when input array(), 0, true
176
     * @expect false when input array(), false, false
177
     * @expect true when input array(), true, false
178
     * @expect true when input array(), 1, false
179
     * @expect false when input array(), '', false
180
     * @expect false when input array(), array(), false
181
     * @expect true when input array(), array(''), false
182
     * @expect true when input array(), array(0), false
183
     */
184 58
    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...
185 58
        return ($v !== null) && ($v !== false) && ($zero || ($v !== 0) && ($v !== 0.0)) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
186
    }
187
188
    /**
189
     * LightnCandy runtime method for {{^var}} inverted section.
190
     *
191
     * @param array<string,array|string|integer> $cx render time context
192
     * @param array<array|string|integer>|string|integer|null $v value to be tested
193
     *
194
     * @return boolean Return true when the value is not null nor false.
195
     *
196
     * @expect true when input array(), null
197
     * @expect false when input array(), 0
198
     * @expect true when input array(), false
199
     * @expect false when input array(), 'false'
200
     * @expect true when input array(), array()
201
     * @expect false when input array(), array('1')
202
     */
203 36
    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...
204 36
        return ($v === null) || ($v === false) || (is_array($v) && (count($v) === 0));
205
    }
206
207
    /**
208
     * LightnCandy runtime method for {{{var}}} .
209
     *
210
     * @param array<string,array|string|integer> $cx render time context
211
     * @param array<array|string|integer>|string|integer|null $v value to be output
212
     *
213
     * @return string The raw value of the specified variable
214
     *
215
     * @expect true when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), true
216
     * @expect 'true' when input array('flags' => array('jstrue' => 1)), true
217
     * @expect '' when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), false
218
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false
219
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true
220
     * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b')
221
     * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', 'b')
222
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b')
223
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b')
224
     * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', true)
225
     * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',true)
226
     * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
227
     * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
228
     */
229 289
    public static function raw($cx, $v) {
230 289
        if ($v === true) {
231 1
            if ($cx['flags']['jstrue']) {
232 1
                return 'true';
233
            }
234
        }
235
236 289
        if (($v === false)) {
237 6
            if ($cx['flags']['jstrue']) {
238 6
                return 'false';
239
            }
240
        }
241
242 284
        if (is_array($v)) {
243 6
            if ($cx['flags']['jsobj']) {
244 6
                if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) {
245 2
                    return '[object Object]';
246
                } else {
247 5
                    $ret = array();
248 5
                    foreach ($v as $k => $vv) {
249 5
                        $ret[] = static::raw($cx, $vv);
250
                    }
251 5
                    return join(',', $ret);
252
                }
253
            } else {
254 1
                return 'Array';
255
            }
256
        }
257
258 284
        return "$v";
259
    }
260
261
    /**
262
     * LightnCandy runtime method for {{var}} .
263
     *
264
     * @param array<string,array|string|integer> $cx render time context
265
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
266
     *
267
     * @return string The htmlencoded value of the specified variable
268
     *
269
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
270
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
271
     * @expect 'a&#039;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
272
     */
273 44
    public static function enc($cx, $var) {
274 44
        return htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8');
275
    }
276
277
    /**
278
     * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js .
279
     *
280
     * @param array<string,array|string|integer> $cx render time context
281
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
282
     *
283
     * @return string The htmlencoded value of the specified variable
284
     *
285
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
286
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
287
     * @expect 'a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
288
     * @expect '&#x60;a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), '`a\'b'
289
     */
290 218
    public static function encq($cx, $var) {
291 218
        return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8'))));
292
    }
293
294
    /**
295
     * LightnCandy runtime method for {{#var}} section.
296
     *
297
     * @param array<string,array|string|integer> $cx render time context
298
     * @param array<array|string|integer>|string|integer|null $v value for the section
299
     * @param array<string>|null $bp block parameters
300
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
301
     * @param boolean $each true when rendering #each
302
     * @param Closure $cb callback function to render child context
303
     * @param Closure|null $else callback function to render child context when {{else}}
304
     *
305
     * @return string The rendered string of the section
306
     *
307
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, false, false, function () {return 'A';}
308
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), null, null, null, false, function () {return 'A';}
309
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), true, null, true, false, function () {return 'A';}
310
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function () {return 'A';}
311
     * @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=";}
312
     * @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=";}
313
     * @expect '' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'abc', null, 'abc', true, function ($c, $i) {return "-$i=";}
314
     * @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=";}
315
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'b', null, 'b', false, function ($c, $i) {return count($i);}
316
     * @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);}
317
     * @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);}
318
     * @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);}
319
     * @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';}
320
     * @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';}
321
     * @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';}
322
     * @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';}
323
     * @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';}
324
     * @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';}
325
     * @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';}
326
     * @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';}
327
     * @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';}
328
     * @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';}
329
     * @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;}
330
     * @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'];}
331
     */
332 155
    public static function sec($cx, $v, $bp, $in, $each, $cb, $else = null) {
333 155
        $push = ($in !== $v) || $each;
334
335 155
        $isAry = is_array($v) || ($v instanceof \ArrayObject);
336 155
        $isTrav = $v instanceof \Traversable;
337 155
        $loop = $each;
338 155
        $keys = null;
339 155
        $last = null;
340 155
        $isObj = false;
341
342 155
        if ($isAry && $else !== null && count($v) === 0) {
343 3
            $ret = $else($cx, $in);
344 3
            return $ret;
345
        }
346
347
        // #var, detect input type is object or not
348 153
        if (!$loop && $isAry) {
349 63
            $keys = array_keys($v);
350 63
            $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
351 63
            $isObj = !$loop;
352
        }
353
354 153
        if ($cx['flags']['mustlam'] && ($v instanceof \Closure)) {
355
            static::err($cx, 'Do not support Section Lambdas!');
356
        }
357
358 153
        if (($loop && $isAry) || $isTrav) {
359 100
            if ($each && !$isTrav) {
360
                // Detect input type is object or not when never done once
361 45
                if ($keys == null) {
362 45
                    $keys = array_keys($v);
363 45
                    $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
364
                }
365
            }
366 100
            $ret = array();
367 100
            if ($push) {
368 98
                $cx['scopes'][] = $in;
369
            }
370 100
            $i = 0;
371 100
            if ($cx['flags']['spvar']) {
372 89
                $old_spvar = $cx['sp_vars'];
373 89
                $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $old_spvar, array('_parent' => $old_spvar));
374 89
                if (!$isTrav) {
375 88
                    $last = count($keys) - 1;
376
                }
377
            }
378
379 100
            $isSparceArray = $isObj && (count(array_filter(array_keys($v), 'is_string')) == 0);
380 100
            foreach ($v as $index => $raw) {
1 ignored issue
show
Bug introduced by
The expression $v of type string|integer|null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
381 93
                if ($cx['flags']['spvar']) {
382 83
                    $cx['sp_vars']['first'] = ($i === 0);
383 83
                    $cx['sp_vars']['last'] = ($i == $last);
384 83
                    $cx['sp_vars']['key'] = $index;
385 83
                    $cx['sp_vars']['index'] = $isSparceArray ? $index : $i;
386 83
                    $i++;
387
                }
388 93
                if (isset($bp[0])) {
389 1
                    $raw = static::m($cx, $raw, array($bp[0] => $raw));
390
                }
391 93
                if (isset($bp[1])) {
392 1
                    $raw = static::m($cx, $raw, array($bp[1] => $cx['sp_vars']['index']));
393
                }
394 93
                $ret[] = $cb($cx, $raw);
395
            }
396 99
            if ($cx['flags']['spvar']) {
397 88
                if ($isObj) {
398 14
                    unset($cx['sp_vars']['key']);
399
                } else {
400 76
                    unset($cx['sp_vars']['last']);
401
                }
402 88
                unset($cx['sp_vars']['index']);
403 88
                unset($cx['sp_vars']['first']);
404 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...
405
            }
406 99
            if ($push) {
407 97
                array_pop($cx['scopes']);
408
            }
409 99
            return join('', $ret);
410
        }
411 56
        if ($each) {
412 2
            if ($else !== null) {
413 1
                $ret = $else($cx, $v);
414 1
                return $ret;
415
            }
416 2
            return '';
417
        }
418 55
        if ($isAry) {
419 9
            if ($push) {
420 8
                $cx['scopes'][] = $in;
421
            }
422 9
            $ret = $cb($cx, $v);
423 9
            if ($push) {
424 8
                array_pop($cx['scopes']);
425
            }
426 9
            return $ret;
427
        }
428
429 47
        if ($v === true) {
430 23
            return $cb($cx, $in);
431
        }
432
433 25
        if (($v !== null) && ($v !== false)) {
434 5
            return $cb($cx, $v);
435
        }
436
437 21
        if ($else !== null) {
438 7
            $ret = $else($cx, $in);
439 7
            return $ret;
440
        }
441
442 15
        return '';
443
    }
444
445
    /**
446
     * LightnCandy runtime method for {{#with var}} .
447
     *
448
     * @param array<string,array|string|integer> $cx render time context
449
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
450
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
451
     * @param array<string>|null $bp block parameters
452
     * @param Closure $cb callback function to render child context
453
     * @param Closure|null $else callback function to render child context when {{else}}
454
     *
455
     * @return string The rendered string of the token
456
     *
457
     * @expect '' when input array(), false, null, false, function () {return 'A';}
458
     * @expect '' when input array(), null, null, null, function () {return 'A';}
459
     * @expect '{"a":"b"}' when input array(), array('a'=>'b'), null, array('a'=>'c'), function ($c, $i) {return json_encode($i);}
460
     * @expect '-b=' when input array(), 'b', null, array('a'=>'b'), function ($c, $i) {return "-$i=";}
461
     */
462 18
    public static function wi($cx, $v, $bp, $in, $cb, $else = null) {
463 18
        if (isset($bp[0])) {
464 2
            $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...
465
        }
466 18
        if (($v === false) || ($v === null)) {
467 4
            return $else ? $else($cx, $in) : '';
468
        }
469 15
        $cx['scopes'][] = $in;
470 15
        $ret = $cb($cx, $v);
471 15
        array_pop($cx['scopes']);
472 15
        return $ret;
473
    }
474
475
    /**
476
     * LightnCandy runtime method to get merged context
477
     *
478
     * @param array<string,array|string|integer> $cx render time context
479
     * @param array<array|string|integer>|string|integer|null $a the context to be merged
480
     * @param array<array|string|integer>|string|integer|null $b the new context to overwrite
481
     *
482
     * @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...
483
     *
484
     */
485 61
    public static function m($cx, $a, $b) {
486 61
        if (is_array($b)) {
487 61
            if ($a === null) {
488 6
                return $b;
489 55
            } else if (is_array($a)) {
490 52
                return array_merge($a, $b);
491 3
            } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($a)) {
492 1
                foreach ($b as $i => $v) {
493 1
                    $a->$i = $v;
494
                }
495
            }
496
        }
497 3
        return $a;
498
    }
499
500
    /**
501
     * LightnCandy runtime method for {{> partial}} .
502
     *
503
     * @param array<string,array|string|integer> $cx render time context
504
     * @param string $p partial name
505
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
506
     *
507
     * @return string The rendered string of the partial
508
     *
509
     */
510 56
    public static function p($cx, $p, $v, $sp = '') {
511 56
        if (!isset($cx['partials'][$p])) {
512 1
            static::err($cx, "Can not find partial named as '$p' !!");
513
            return '';
514
        }
515
516 55
        return call_user_func($cx['partials'][$p], $cx, static::m($cx, $v[0][0], $v[1]), $sp);
517
    }
518
519
    /**
520
     * LightnCandy runtime method for custom helpers.
521
     *
522
     * @param array<string,array|string|integer> $cx render time context
523
     * @param string $ch the name of custom helper to be executed
524
     * @param array<array> $vars variables for the helper
525
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
526
     *
527
     * @return string The rendered string of the token
528
     *
529
     * @expect '---' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('-'),array()), 'raw'
530
     * @expect '-&amp;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('&'),array()), 'enc'
531
     * @expect '-&#x27;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('\''),array()), 'encq'
532
     * @expect '-b-' when input array('helpers' => array('a' => function ($i,$j) {return "-{$j['a']}-";})), 'a', array(array(),array('a' => 'b')), 'raw'
533
     */
534 22
    public static function ch($cx, $ch, $vars, $op) {
535 22
        return static::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op);
536
    }
537
538
    /**
539
     * LightnCandy runtime method to handle response of custom helpers.
540
     *
541
     * @param string|array<string,array|string|integer> $ret return value from custom helper
542
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
543
     *
544
     * @return string The rendered string of the token
545
     *
546
     * @expect '-&-' when input '-&-', 'raw'
547
     * @expect '-&amp;&#039;-' when input '-&\'-', 'enc'
548
     * @expect '-&amp;&#x27;-' when input '-&\'-', 'encq'
549
     * @expect '-&amp;&#039;-' when input array('-&\'-'), 'enc'
550
     * @expect '-&amp;&#x27;-' when input array('-&\'-'), 'encq'
551
     * @expect '-&amp;-' when input array('-&-', false), 'enc'
552
     * @expect '-&-' when input array('-&-', false), 'raw'
553
     * @expect '-&-' when input array('-&-', 'raw'), 'enc'
554
     * @expect '-&amp;&#x27;-' when input array('-&\'-', 'encq'), 'raw'
555
     */
556 150
    public static function chret($ret, $op) {
557 150
        if (is_array($ret)) {
558 10
            if (isset($ret[1]) && $ret[1]) {
559 9
                $op = $ret[1];
560
            }
561 10
            $ret = $ret[0];
562
        }
563
564
        switch ($op) {
565 150
            case 'enc':
566 9
                return htmlentities($ret, ENT_QUOTES, 'UTF-8');
567 92
            case 'encq':
568 76
                return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities($ret, ENT_QUOTES, 'UTF-8'))));
569
        }
570 92
        return $ret;
571
    }
572
573
    /**
574
     * LightnCandy runtime method for Handlebars.js style custom helpers.
575
     *
576
     * @param array<string,array|string|integer> $cx render time context
577
     * @param string $ch the name of custom helper to be executed
578
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
579
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
580
     * @param boolean $inverted the logic will be inverted
581
     * @param Closure|null $cb callback function to render child context
582
     * @param Closure|null $else callback function to render child context when {{else}}
583
     *
584
     * @return string The rendered string of the token
585
     */
586 130
    public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
587 130
        $isBlock = (is_object($cb) && ($cb instanceof \Closure));
588
589 130
        if (isset($cx['blparam'][0][$ch])) {
590 1
            return $cx['blparam'][0][$ch];
591
        }
592
593 130
        $args = $vars[0];
594
        $options = array(
595 130
            'name' => $ch,
596 130
            'hash' => $vars[1],
597 130
            'fn.blockParams' => 0,
598
        );
599
600 130
        if ($isBlock) {
601 52
            $options['_this'] = &$op;
602
        } else {
603 85
            $options['_this'] = &$inverted;
604
        }
605
606 130
        if (isset($vars[2])) {
607 3
            $options['fn.blockParams'] = count($vars[2]);
608
        }
609
610
        // $invert the logic
611 130
        if ($inverted) {
612 59
            $tmp = $else;
613 59
            $else = $cb;
614 59
            $cb = $tmp;
615
        }
616
617 130
        if ($isBlock) {
618
            $options['fn'] = function ($context = '_NO_INPUT_HERE_', $data = null) use ($cx, &$op, $cb, $options, $vars) {
619 41
                if ($cx['flags']['echo']) {
620 6
                    ob_start();
621
                }
622 41
                if (isset($data['data'])) {
623 6
                    $old_spvar = $cx['sp_vars'];
624 6
                    $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $data['data'], array('_parent' => $old_spvar));
625
                }
626 41
                $ex = false;
627 41
                if (isset($data['blockParams']) && isset($vars[2])) {
628 3
                    $ex = array_combine($vars[2], array_slice($data['blockParams'], 0, count($vars[2])));
629 3
                    array_unshift($cx['blparam'], $ex);
630 39
                } else if (isset($cx['blparam'][0])) {
631 1
                    $ex = $cx['blparam'][0];
632
                }
633 41
                if (($context === '_NO_INPUT_HERE_') || ($context === $op)) {
634 24
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $op, $ex) : $op);
635
                } else {
636 19
                    $cx['scopes'][] = $op;
637 19
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $context, $ex) : $context);
638 19
                    array_pop($cx['scopes']);
639
                }
640 41
                if (isset($data['data'])) {
641 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...
642
                }
643 41
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
644
            };
645
        }
646
647 130
        if ($else) {
648 7
            $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
649 7
                if ($cx['flags']['echo']) {
650 3
                    ob_start();
651
                }
652 7
                if ($context === '_NO_INPUT_HERE_') {
653 2
                    $ret = $else($cx, $op);
654
                } else {
655 5
                    $cx['scopes'][] = $op;
656 5
                    $ret = $else($cx, $context);
657 5
                    array_pop($cx['scopes']);
658
                }
659 7
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
660
            };
661
        }
662
663 130
        if ($cx['flags']['spvar']) {
664 119
            $options['data'] = $cx['sp_vars'];
665
        }
666
667 130
        $args[] = $options;
668 130
        $e = null;
669 130
        $r = true;
670
671
        try {
672 130
            $r = call_user_func_array($cx['hbhelpers'][$ch], $args);
673 2
        } catch (\Exception $E) {
674 2
            $e = "Runtime: call custom helper '$ch' error: " . $E->getMessage();
675
        }
676
677 130
        if($e !== null) {
678 2
            static::err($cx, $e);
679
        }
680
681 129
        return static::chret($r, $isBlock ? 'raw' : $op);
682
    }
683
684
    /**
685
     * LightnCandy runtime method for block custom helpers.
686
     *
687
     * @param array<string,array|string|integer> $cx render time context
688
     * @param string $ch the name of custom helper to be executed
689
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
690
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
691
     * @param boolean $inverted the logic will be inverted
692
     * @param Closure $cb callback function to render child context
693
     * @param Closure|null $else callback function to render child context when {{else}}
694
     *
695
     * @return string The rendered string of the token
696
     *
697
     * @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);}
698
     * @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);}
699
     * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
700
     */
701 4
    public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = null) {
702 4
        $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]);
703
704
        // $invert the logic
705 4
        if ($inverted) {
706 2
            $tmp = $else;
707 2
            $else = $cb;
708 2
            $cb = $tmp;
709
        }
710
711 4
        $ret = '';
712 4
        if ($r === null) {
713 2
            if ($else) {
714 1
                $cx['scopes'][] = $in;
715 1
                $ret = $else($cx, $r);
716 2
                array_pop($cx['scopes']);
717
            }
718
        } else {
719 3
            if ($cb) {
720 3
                $cx['scopes'][] = $in;
721 3
                $ret = $cb($cx, $r);
722 3
                array_pop($cx['scopes']);
723
            }
724
        }
725
726 4
        return $ret;
727
    }
728
}
729
730