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 ( dbf016...e6ca53 )
by Zordius
03:50
created

Runtime::isec()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 9.2
cc 4
eloc 2
nc 5
nop 2
crap 4
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
     * @param array $args extra arguments for lambda
0 ignored issues
show
Documentation introduced by
Should the type for parameter $args not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
111
     *
112
     * @return null|string Return the value or null when not found
113
     *
114
     * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), null, 0, array('a', 'b')
115
     * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), null, array('a' => array('b' => 3)), array('a', 'b')
116
     * @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')
117
     * @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')
118
     */
119 358
    public static function v($cx, $in, $base, $path, $args = null) {
120 358
        $count = count($cx['scopes']);
121 358
        while ($base) {
122 327
            $v = $base;
123 327
            foreach ($path as $name) {
124 327
                if (is_array($v) && isset($v[$name])) {
125 322
                    $v = $v[$name];
126 322
                    continue;
127
                }
128 32
                if (is_object($v)) {
129 5
                    if ($cx['flags']['prop'] && !($v instanceof \Closure) && isset($v->$name)) {
130 3
                        $v = $v->$name;
131 3
                        continue;
132
                    }
133 5
                    if ($cx['flags']['method'] && is_callable(array($v, $name))) {
134 4
                        $v = $v->$name();
135 4
                        continue;
136
                    }
137
                }
138 30
                if ($cx['flags']['mustlok']) {
139 27
                    unset($v);
140 27
                    break;
141
                }
142 3
                return null;
143
            }
144 327
            if (isset($v)) {
145 316
                if ($v instanceof \Closure) {
146 23
                    if ($cx['flags']['mustlam'] || $cx['flags']['lambda']) {
147 23
                        if ($args) {
148 5
                            $A = $args[0];
149 5
                            $A[] = array('hash' => $args[1]);
150 5
                            $v = call_user_func_array($v, $A);
151
                        } else {
152 18
                            $v = $v($in);
153
                        }
154
                    }
155
                }
156 316
                return $v;
157
            }
158 27
            $count--;
159
            switch ($count) {
160 27
                case -1:
161 26
                    $base = $cx['sp_vars']['root'];
162 26
                    break;
163 5
                case -2:
164 25
                    return null;
165
                default:
166 5
                    $base = $cx['scopes'][$count];
167
            }
168
        }
169 35
    }
170
171
    /**
172
     * LightnCandy runtime method for {{#if var}}.
173
     *
174
     * @param array<string,array|string|integer> $cx render time context
175
     * @param array<array|string|integer>|string|integer|null $v value to be tested
176
     * @param boolean $zero include zero as true
177
     *
178
     * @return boolean Return true when the value is not null nor false.
179
     *
180
     * @expect false when input array(), null, false
181
     * @expect false when input array(), 0, false
182
     * @expect true when input array(), 0, true
183
     * @expect false when input array(), false, false
184
     * @expect true when input array(), true, false
185
     * @expect true when input array(), 1, false
186
     * @expect false when input array(), '', false
187
     * @expect false when input array(), array(), false
188
     * @expect true when input array(), array(''), false
189
     * @expect true when input array(), array(0), false
190
     */
191 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...
192 58
        return ($v !== null) && ($v !== false) && ($zero || ($v !== 0) && ($v !== 0.0)) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
193
    }
194
195
    /**
196
     * LightnCandy runtime method for {{^var}} inverted section.
197
     *
198
     * @param array<string,array|string|integer> $cx render time context
199
     * @param array<array|string|integer>|string|integer|null $v value to be tested
200
     *
201
     * @return boolean Return true when the value is not null nor false.
202
     *
203
     * @expect true when input array(), null
204
     * @expect false when input array(), 0
205
     * @expect true when input array(), false
206
     * @expect false when input array(), 'false'
207
     * @expect true when input array(), array()
208
     * @expect false when input array(), array('1')
209
     */
210 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...
211 36
        return ($v === null) || ($v === false) || (is_array($v) && (count($v) === 0));
212
    }
213
214
    /**
215
     * LightnCandy runtime method for {{{var}}} .
216
     *
217
     * @param array<string,array|string|integer> $cx render time context
218
     * @param array<array|string|integer>|string|integer|null $v value to be output
219
     *
220
     * @return string The raw value of the specified variable
221
     *
222
     * @expect true when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), true
223
     * @expect 'true' when input array('flags' => array('jstrue' => 1)), true
224
     * @expect '' when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), false
225
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false
226
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true
227
     * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b')
228
     * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', 'b')
229
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b')
230
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b')
231
     * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', true)
232
     * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',true)
233
     * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
234
     * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
235
     */
236 292
    public static function raw($cx, $v) {
237 292
        if ($v === true) {
238 1
            if ($cx['flags']['jstrue']) {
239 1
                return 'true';
240
            }
241
        }
242
243 292
        if (($v === false)) {
244 6
            if ($cx['flags']['jstrue']) {
245 6
                return 'false';
246
            }
247
        }
248
249 287
        if (is_array($v)) {
250 6
            if ($cx['flags']['jsobj']) {
251 6
                if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) {
252 2
                    return '[object Object]';
253
                } else {
254 5
                    $ret = array();
255 5
                    foreach ($v as $k => $vv) {
256 5
                        $ret[] = static::raw($cx, $vv);
257
                    }
258 5
                    return join(',', $ret);
259
                }
260
            } else {
261 1
                return 'Array';
262
            }
263
        }
264
265 287
        return "$v";
266
    }
267
268
    /**
269
     * LightnCandy runtime method for {{var}} .
270
     *
271
     * @param array<string,array|string|integer> $cx render time context
272
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
273
     *
274
     * @return string The htmlencoded value of the specified variable
275
     *
276
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
277
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
278
     * @expect 'a&#039;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
279
     */
280 44
    public static function enc($cx, $var) {
281 44
        return htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8');
282
    }
283
284
    /**
285
     * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js .
286
     *
287
     * @param array<string,array|string|integer> $cx render time context
288
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
289
     *
290
     * @return string The htmlencoded value of the specified variable
291
     *
292
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
293
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
294
     * @expect 'a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
295
     * @expect '&#x60;a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), '`a\'b'
296
     */
297 221
    public static function encq($cx, $var) {
298 221
        return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8'))));
299
    }
300
301
    /**
302
     * LightnCandy runtime method for {{#var}} section.
303
     *
304
     * @param array<string,array|string|integer> $cx render time context
305
     * @param array<array|string|integer>|string|integer|null $v value for the section
306
     * @param array<string>|null $bp block parameters
307
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
308
     * @param boolean $each true when rendering #each
309
     * @param Closure $cb callback function to render child context
310
     * @param Closure|null $else callback function to render child context when {{else}}
311
     *
312
     * @return string The rendered string of the section
313
     *
314
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, false, false, function () {return 'A';}
315
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), null, null, null, false, function () {return 'A';}
316
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), true, null, true, false, function () {return 'A';}
317
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function () {return 'A';}
318
     * @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=";}
319
     * @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=";}
320
     * @expect '' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'abc', null, 'abc', true, function ($c, $i) {return "-$i=";}
321
     * @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=";}
322
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'b', null, 'b', false, function ($c, $i) {return count($i);}
323
     * @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);}
324
     * @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);}
325
     * @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);}
326
     * @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';}
327
     * @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';}
328
     * @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';}
329
     * @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';}
330
     * @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';}
331
     * @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';}
332
     * @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';}
333
     * @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';}
334
     * @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';}
335
     * @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';}
336
     * @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;}
337
     * @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'];}
338
     */
339 155
    public static function sec($cx, $v, $bp, $in, $each, $cb, $else = null) {
340 155
        $push = ($in !== $v) || $each;
341
342 155
        $isAry = is_array($v) || ($v instanceof \ArrayObject);
343 155
        $isTrav = $v instanceof \Traversable;
344 155
        $loop = $each;
345 155
        $keys = null;
346 155
        $last = null;
347 155
        $isObj = false;
348
349 155
        if ($isAry && $else !== null && count($v) === 0) {
350 3
            $ret = $else($cx, $in);
351 3
            return $ret;
352
        }
353
354
        // #var, detect input type is object or not
355 153
        if (!$loop && $isAry) {
356 63
            $keys = array_keys($v);
357 63
            $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
358 63
            $isObj = !$loop;
359
        }
360
361 153
        if ($cx['flags']['mustlam'] && ($v instanceof \Closure)) {
362
            static::err($cx, 'Do not support Section Lambdas!');
363
        }
364
365 153
        if (($loop && $isAry) || $isTrav) {
366 100
            if ($each && !$isTrav) {
367
                // Detect input type is object or not when never done once
368 45
                if ($keys == null) {
369 45
                    $keys = array_keys($v);
370 45
                    $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
371
                }
372
            }
373 100
            $ret = array();
374 100
            if ($push) {
375 98
                $cx['scopes'][] = $in;
376
            }
377 100
            $i = 0;
378 100
            if ($cx['flags']['spvar']) {
379 89
                $old_spvar = $cx['sp_vars'];
380 89
                $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $old_spvar, array('_parent' => $old_spvar));
381 89
                if (!$isTrav) {
382 88
                    $last = count($keys) - 1;
383
                }
384
            }
385
386 100
            $isSparceArray = $isObj && (count(array_filter(array_keys($v), 'is_string')) == 0);
387 100
            foreach ($v as $index => $raw) {
388 93
                if ($cx['flags']['spvar']) {
389 83
                    $cx['sp_vars']['first'] = ($i === 0);
390 83
                    $cx['sp_vars']['last'] = ($i == $last);
391 83
                    $cx['sp_vars']['key'] = $index;
392 83
                    $cx['sp_vars']['index'] = $isSparceArray ? $index : $i;
393 83
                    $i++;
394
                }
395 93
                if (isset($bp[0])) {
396 1
                    $raw = static::m($cx, $raw, array($bp[0] => $raw));
397
                }
398 93
                if (isset($bp[1])) {
399 1
                    $raw = static::m($cx, $raw, array($bp[1] => $cx['sp_vars']['index']));
400
                }
401 93
                $ret[] = $cb($cx, $raw);
402
            }
403 99
            if ($cx['flags']['spvar']) {
404 88
                if ($isObj) {
405 14
                    unset($cx['sp_vars']['key']);
406
                } else {
407 76
                    unset($cx['sp_vars']['last']);
408
                }
409 88
                unset($cx['sp_vars']['index']);
410 88
                unset($cx['sp_vars']['first']);
411 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...
412
            }
413 99
            if ($push) {
414 97
                array_pop($cx['scopes']);
415
            }
416 99
            return join('', $ret);
417
        }
418 56
        if ($each) {
419 2
            if ($else !== null) {
420 1
                $ret = $else($cx, $v);
421 1
                return $ret;
422
            }
423 2
            return '';
424
        }
425 55
        if ($isAry) {
426 9
            if ($push) {
427 8
                $cx['scopes'][] = $in;
428
            }
429 9
            $ret = $cb($cx, $v);
430 9
            if ($push) {
431 8
                array_pop($cx['scopes']);
432
            }
433 9
            return $ret;
434
        }
435
436 47
        if ($v === true) {
437 23
            return $cb($cx, $in);
438
        }
439
440 25
        if (($v !== null) && ($v !== false)) {
441 5
            return $cb($cx, $v);
442
        }
443
444 21
        if ($else !== null) {
445 7
            $ret = $else($cx, $in);
446 7
            return $ret;
447
        }
448
449 15
        return '';
450
    }
451
452
    /**
453
     * LightnCandy runtime method for {{#with var}} .
454
     *
455
     * @param array<string,array|string|integer> $cx render time context
456
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
457
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
458
     * @param array<string>|null $bp block parameters
459
     * @param Closure $cb callback function to render child context
460
     * @param Closure|null $else callback function to render child context when {{else}}
461
     *
462
     * @return string The rendered string of the token
463
     *
464
     * @expect '' when input array(), false, null, false, function () {return 'A';}
465
     * @expect '' when input array(), null, null, null, function () {return 'A';}
466
     * @expect '{"a":"b"}' when input array(), array('a'=>'b'), null, array('a'=>'c'), function ($c, $i) {return json_encode($i);}
467
     * @expect '-b=' when input array(), 'b', null, array('a'=>'b'), function ($c, $i) {return "-$i=";}
468
     */
469 18
    public static function wi($cx, $v, $bp, $in, $cb, $else = null) {
470 18
        if (isset($bp[0])) {
471 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...
472
        }
473 18
        if (($v === false) || ($v === null)) {
474 4
            return $else ? $else($cx, $in) : '';
475
        }
476 15
        $cx['scopes'][] = $in;
477 15
        $ret = $cb($cx, $v);
478 15
        array_pop($cx['scopes']);
479 15
        return $ret;
480
    }
481
482
    /**
483
     * LightnCandy runtime method to get merged context
484
     *
485
     * @param array<string,array|string|integer> $cx render time context
486
     * @param array<array|string|integer>|string|integer|null $a the context to be merged
487
     * @param array<array|string|integer>|string|integer|null $b the new context to overwrite
488
     *
489
     * @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...
490
     *
491
     */
492 61
    public static function m($cx, $a, $b) {
493 61
        if (is_array($b)) {
494 61
            if ($a === null) {
495 6
                return $b;
496 55
            } else if (is_array($a)) {
497 52
                return array_merge($a, $b);
498 3
            } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($a)) {
499 1
                foreach ($b as $i => $v) {
500 1
                    $a->$i = $v;
501
                }
502
            }
503
        }
504 3
        return $a;
505
    }
506
507
    /**
508
     * LightnCandy runtime method for {{> partial}} .
509
     *
510
     * @param array<string,array|string|integer> $cx render time context
511
     * @param string $p partial name
512
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
513
     *
514
     * @return string The rendered string of the partial
515
     *
516
     */
517 56
    public static function p($cx, $p, $v, $sp = '') {
518 56
        if (!isset($cx['partials'][$p])) {
519 1
            static::err($cx, "Can not find partial named as '$p' !!");
520
            return '';
521
        }
522
523 55
        return call_user_func($cx['partials'][$p], $cx, static::m($cx, $v[0][0], $v[1]), $sp);
524
    }
525
526
    /**
527
     * LightnCandy runtime method for custom helpers.
528
     *
529
     * @param array<string,array|string|integer> $cx render time context
530
     * @param string $ch the name of custom helper to be executed
531
     * @param array<array> $vars variables for the helper
532
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
533
     *
534
     * @return string The rendered string of the token
535
     *
536
     * @expect '---' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('-'),array()), 'raw'
537
     * @expect '-&amp;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('&'),array()), 'enc'
538
     * @expect '-&#x27;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('\''),array()), 'encq'
539
     * @expect '-b-' when input array('helpers' => array('a' => function ($i,$j) {return "-{$j['a']}-";})), 'a', array(array(),array('a' => 'b')), 'raw'
540
     */
541 22
    public static function ch($cx, $ch, $vars, $op) {
542 22
        return static::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op);
543
    }
544
545
    /**
546
     * LightnCandy runtime method to handle response of custom helpers.
547
     *
548
     * @param string|array<string,array|string|integer> $ret return value from custom helper
549
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
550
     *
551
     * @return string The rendered string of the token
552
     *
553
     * @expect '-&-' when input '-&-', 'raw'
554
     * @expect '-&amp;&#039;-' when input '-&\'-', 'enc'
555
     * @expect '-&amp;&#x27;-' when input '-&\'-', 'encq'
556
     * @expect '-&amp;&#039;-' when input array('-&\'-'), 'enc'
557
     * @expect '-&amp;&#x27;-' when input array('-&\'-'), 'encq'
558
     * @expect '-&amp;-' when input array('-&-', false), 'enc'
559
     * @expect '-&-' when input array('-&-', false), 'raw'
560
     * @expect '-&-' when input array('-&-', 'raw'), 'enc'
561
     * @expect '-&amp;&#x27;-' when input array('-&\'-', 'encq'), 'raw'
562
     */
563 160
    public static function chret($ret, $op) {
564 160
        if (is_array($ret)) {
565 10
            if (isset($ret[1]) && $ret[1]) {
566 9
                $op = $ret[1];
567
            }
568 10
            $ret = $ret[0];
569
        }
570
571
        switch ($op) {
572 160
            case 'enc':
573 9
                return htmlentities($ret, ENT_QUOTES, 'UTF-8');
574 98
            case 'encq':
575 83
                return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities($ret, ENT_QUOTES, 'UTF-8'))));
576
        }
577 98
        return $ret;
578
    }
579
580
    /**
581
     * LightnCandy runtime method for Handlebars.js style custom helpers.
582
     *
583
     * @param array<string,array|string|integer> $cx render time context
584
     * @param string $ch the name of custom helper to be executed
585
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
586
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
587
     * @param boolean $inverted the logic will be inverted
588
     * @param Closure|null $cb callback function to render child context
589
     * @param Closure|null $else callback function to render child context when {{else}}
590
     *
591
     * @return string The rendered string of the token
592
     */
593 140
    public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
594 140
        $isBlock = (is_object($cb) && ($cb instanceof \Closure));
595
596 140
        if (isset($cx['blparam'][0][$ch])) {
597 1
            return $cx['blparam'][0][$ch];
598
        }
599
600 140
        $args = $vars[0];
601
        $options = array(
602 140
            'name' => $ch,
603 140
            'hash' => $vars[1],
604 140
            'contexts' => count($cx['scopes']) ? $cx['scopes'] : array(null),
605 140
            'fn.blockParams' => 0,
606
        );
607
608 140
        if ($isBlock) {
609 57
            $options['_this'] = &$op;
610
        } else {
611 92
            $options['_this'] = &$inverted;
612
        }
613
614 140
        if (isset($vars[2])) {
615 3
            $options['fn.blockParams'] = count($vars[2]);
616
        }
617
618
        // $invert the logic
619 140
        if ($inverted) {
620 60
            $tmp = $else;
621 60
            $else = $cb;
622 60
            $cb = $tmp;
623
        }
624
625 140
        if ($isBlock) {
626
            $options['fn'] = function ($context = '_NO_INPUT_HERE_', $data = null) use ($cx, &$op, $cb, $options, $vars) {
627 45
                if ($cx['flags']['echo']) {
628 6
                    ob_start();
629
                }
630 45
                if (isset($data['data'])) {
631 6
                    $old_spvar = $cx['sp_vars'];
632 6
                    $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $data['data'], array('_parent' => $old_spvar));
633
                }
634 45
                $ex = false;
635 45
                if (isset($data['blockParams']) && isset($vars[2])) {
636 3
                    $ex = array_combine($vars[2], array_slice($data['blockParams'], 0, count($vars[2])));
637 3
                    array_unshift($cx['blparam'], $ex);
638 43
                } else if (isset($cx['blparam'][0])) {
639 1
                    $ex = $cx['blparam'][0];
640
                }
641 45
                if (($context === '_NO_INPUT_HERE_') || ($context === $op)) {
642 26
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $op, $ex) : $op);
643
                } else {
644 22
                    $cx['scopes'][] = $op;
645 22
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $context, $ex) : $context);
646 22
                    array_pop($cx['scopes']);
647
                }
648 45
                if (isset($data['data'])) {
649 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...
650
                }
651 45
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
652
            };
653
        }
654
655 140
        if ($else) {
656 7
            $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
657 7
                if ($cx['flags']['echo']) {
658 3
                    ob_start();
659
                }
660 7
                if ($context === '_NO_INPUT_HERE_') {
661 2
                    $ret = $else($cx, $op);
662
                } else {
663 5
                    $cx['scopes'][] = $op;
664 5
                    $ret = $else($cx, $context);
665 5
                    array_pop($cx['scopes']);
666
                }
667 7
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
668
            };
669
        }
670
671 140
        if ($cx['flags']['spvar']) {
672 129
            $options['data'] = $cx['sp_vars'];
673
        }
674
675 140
        $args[] = $options;
676 140
        $e = null;
677 140
        $r = true;
678
679
        try {
680 140
            $r = call_user_func_array($cx['hbhelpers'][$ch], $args);
681 2
        } catch (\Exception $E) {
682 2
            $e = "Runtime: call custom helper '$ch' error: " . $E->getMessage();
683
        }
684
685 140
        if($e !== null) {
686 2
            static::err($cx, $e);
687
        }
688
689 139
        return static::chret($r, $isBlock ? 'raw' : $op);
690
    }
691
692
    /**
693
     * LightnCandy runtime method for block custom helpers.
694
     *
695
     * @param array<string,array|string|integer> $cx render time context
696
     * @param string $ch the name of custom helper to be executed
697
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
698
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
699
     * @param boolean $inverted the logic will be inverted
700
     * @param Closure $cb callback function to render child context
701
     * @param Closure|null $else callback function to render child context when {{else}}
702
     *
703
     * @return string The rendered string of the token
704
     *
705
     * @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);}
706
     * @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);}
707
     * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
708
     */
709 4
    public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = null) {
710 4
        $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]);
711
712
        // $invert the logic
713 4
        if ($inverted) {
714 2
            $tmp = $else;
715 2
            $else = $cb;
716 2
            $cb = $tmp;
717
        }
718
719 4
        $ret = '';
720 4
        if ($r === null) {
721 2
            if ($else) {
722 1
                $cx['scopes'][] = $in;
723 1
                $ret = $else($cx, $r);
724 2
                array_pop($cx['scopes']);
725
            }
726
        } else {
727 3
            if ($cb) {
728 3
                $cx['scopes'][] = $in;
729 3
                $ret = $cb($cx, $r);
730 3
                array_pop($cx['scopes']);
731
            }
732
        }
733
734 4
        return $ret;
735
    }
736
}
737
738