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 ( 4347a7...178f3d )
by Zordius
02:13
created

Runtime::debug()   F

Complexity

Conditions 14
Paths 705

Size

Total Lines 30
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 14

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
ccs 20
cts 20
cp 1
rs 2.9876
cc 14
eloc 23
nc 705
nop 3
crap 14

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 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|null $args extra arguments for lambda
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 382
    public static function v($cx, $in, $base, $path, $args = null) {
120 382
        $count = count($cx['scopes']);
121 382
        while ($base) {
122 346
            $v = $base;
123 346
            foreach ($path as $name) {
124 346
                if (is_array($v) && isset($v[$name])) {
125 341
                    $v = $v[$name];
126 341
                    continue;
127
                }
128 33
                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 31
                if ($cx['flags']['mustlok']) {
139 28
                    unset($v);
140 28
                    break;
141
                }
142 3
                return null;
143
            }
144 346
            if (isset($v)) {
145 335
                if ($v instanceof \Closure) {
146 30
                    if ($cx['flags']['mustlam'] || $cx['flags']['lambda']) {
147 30
                        if (!$cx['flags']['knohlp'] && ($args || ($args === 0))) {
148 20
                            $A = $args ? $args[0] : array();
149 20
                            $A[] = array('hash' => $args[1], '_this' => $in);
150
                        } else {
151 11
                            $A = array($in);
152
                        }
153 30
                        $v = call_user_func_array($v, $A);
154
                    }
155
                }
156 335
                return $v;
157
            }
158 28
            $count--;
159
            switch ($count) {
160 28
                case -1:
161 27
                    $base = $cx['sp_vars']['root'];
162 27
                    break;
163 6
                case -2:
164 26
                    return null;
165
                default:
166 6
                    $base = $cx['scopes'][$count];
167
            }
168
        }
169 40
        if ($args) {
170 3
            static::err($cx, 'Can not find helper or lambda: "' . implode('.', $path) . '" !');
171
        }
172 40
    }
173
174
    /**
175
     * LightnCandy runtime method for {{#if var}}.
176
     *
177
     * @param array<string,array|string|integer> $cx render time context
178
     * @param array<array|string|integer>|string|integer|null $v value to be tested
179
     * @param boolean $zero include zero as true
180
     *
181
     * @return boolean Return true when the value is not null nor false.
182
     *
183
     * @expect false when input array(), null, false
184
     * @expect false when input array(), 0, false
185
     * @expect true when input array(), 0, true
186
     * @expect false when input array(), false, false
187
     * @expect true when input array(), true, false
188
     * @expect true when input array(), 1, false
189
     * @expect false when input array(), '', false
190
     * @expect false when input array(), array(), false
191
     * @expect true when input array(), array(''), false
192
     * @expect true when input array(), array(0), false
193
     */
194 59
    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...
195 59
        return ($v !== null) && ($v !== false) && ($zero || ($v !== 0) && ($v !== 0.0)) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
196
    }
197
198
    /**
199
     * LightnCandy runtime method for {{^var}} inverted section.
200
     *
201
     * @param array<string,array|string|integer> $cx render time context
202
     * @param array<array|string|integer>|string|integer|null $v value to be tested
203
     *
204
     * @return boolean Return true when the value is not null nor false.
205
     *
206
     * @expect true when input array(), null
207
     * @expect false when input array(), 0
208
     * @expect true when input array(), false
209
     * @expect false when input array(), 'false'
210
     * @expect true when input array(), array()
211
     * @expect false when input array(), array('1')
212
     */
213 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...
214 37
        return ($v === null) || ($v === false) || (is_array($v) && (count($v) === 0));
215
    }
216
217
    /**
218
     * LightnCandy runtime method for {{{var}}} .
219
     *
220
     * @param array<string,array|string|integer> $cx render time context
221
     * @param array<array|string|integer>|string|integer|null $v value to be output
222
     *
223
     * @return string The raw value of the specified variable
224
     *
225
     * @expect true when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), true
226
     * @expect 'true' when input array('flags' => array('jstrue' => 1)), true
227
     * @expect '' when input array('flags' => array('jstrue' => 0, 'mustlam' => 0, 'lambda' => 0)), false
228
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false
229
     * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true
230
     * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b')
231
     * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', 'b')
232
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b')
233
     * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b')
234
     * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a', true)
235
     * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',true)
236
     * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
237
     * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1, 'mustlam' => 0, 'lambda' => 0)), array('a',false)
238
     */
239 309
    public static function raw($cx, $v) {
240 309
        if ($v === true) {
241 1
            if ($cx['flags']['jstrue']) {
242 1
                return 'true';
243
            }
244
        }
245
246 309
        if (($v === false)) {
247 6
            if ($cx['flags']['jstrue']) {
248 6
                return 'false';
249
            }
250
        }
251
252 304
        if (is_array($v)) {
253 6
            if ($cx['flags']['jsobj']) {
254 6
                if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) {
255 2
                    return '[object Object]';
256
                } else {
257 5
                    $ret = array();
258 5
                    foreach ($v as $k => $vv) {
259 5
                        $ret[] = static::raw($cx, $vv);
260
                    }
261 5
                    return join(',', $ret);
262
                }
263
            } else {
264 1
                return 'Array';
265
            }
266
        }
267
268 304
        return "$v";
269
    }
270
271
    /**
272
     * LightnCandy runtime method for {{var}} .
273
     *
274
     * @param array<string,array|string|integer> $cx render time context
275
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
276
     *
277
     * @return string The htmlencoded value of the specified variable
278
     *
279
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
280
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
281
     * @expect 'a&#039;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
282
     */
283 44
    public static function enc($cx, $var) {
284 44
        return htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8');
285
    }
286
287
    /**
288
     * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js .
289
     *
290
     * @param array<string,array|string|integer> $cx render time context
291
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
292
     *
293
     * @return string The htmlencoded value of the specified variable
294
     *
295
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
296
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
297
     * @expect 'a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
298
     * @expect '&#x60;a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), '`a\'b'
299
     */
300 236
    public static function encq($cx, $var) {
301 236
        return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities(static::raw($cx, $var), ENT_QUOTES, 'UTF-8'))));
302
    }
303
304
    /**
305
     * LightnCandy runtime method for {{#var}} section.
306
     *
307
     * @param array<string,array|string|integer> $cx render time context
308
     * @param array<array|string|integer>|string|integer|null $v value for the section
309
     * @param array<string>|null $bp block parameters
310
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
311
     * @param boolean $each true when rendering #each
312
     * @param Closure $cb callback function to render child context
313
     * @param Closure|null $else callback function to render child context when {{else}}
314
     *
315
     * @return string The rendered string of the section
316
     *
317
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, false, false, function () {return 'A';}
318
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), null, null, null, false, function () {return 'A';}
319
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), true, null, true, false, function () {return 'A';}
320
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function () {return 'A';}
321
     * @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=";}
322
     * @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=";}
323
     * @expect '' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'abc', null, 'abc', true, function ($c, $i) {return "-$i=";}
324
     * @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=";}
325
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'b', null, 'b', false, function ($c, $i) {return count($i);}
326
     * @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);}
327
     * @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);}
328
     * @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);}
329
     * @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';}
330
     * @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';}
331
     * @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';}
332
     * @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';}
333
     * @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';}
334
     * @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';}
335
     * @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';}
336
     * @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';}
337
     * @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';}
338
     * @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';}
339
     * @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;}
340
     * @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'];}
341
     */
342 157
    public static function sec($cx, $v, $bp, $in, $each, $cb, $else = null) {
343 157
        $push = ($in !== $v) || $each;
344
345 157
        $isAry = is_array($v) || ($v instanceof \ArrayObject);
346 157
        $isTrav = $v instanceof \Traversable;
347 157
        $loop = $each;
348 157
        $keys = null;
349 157
        $last = null;
350 157
        $isObj = false;
351
352 157
        if ($isAry && $else !== null && count($v) === 0) {
353 3
            $ret = $else($cx, $in);
354 3
            return $ret;
355
        }
356
357
        // #var, detect input type is object or not
358 155
        if (!$loop && $isAry) {
359 65
            $keys = array_keys($v);
360 65
            $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
361 65
            $isObj = !$loop;
362
        }
363
364 155
        if ($cx['flags']['mustlam'] && ($v instanceof \Closure)) {
365
            static::err($cx, 'Do not support Section Lambdas!');
366
        }
367
368 155
        if (($loop && $isAry) || $isTrav) {
369 100
            if ($each && !$isTrav) {
370
                // Detect input type is object or not when never done once
371 45
                if ($keys == null) {
372 45
                    $keys = array_keys($v);
373 45
                    $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
374
                }
375
            }
376 100
            $ret = array();
377 100
            if ($push) {
378 98
                $cx['scopes'][] = $in;
379
            }
380 100
            $i = 0;
381 100
            if ($cx['flags']['spvar']) {
382 89
                $old_spvar = $cx['sp_vars'];
383 89
                $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $old_spvar, array('_parent' => $old_spvar));
384 89
                if (!$isTrav) {
385 88
                    $last = count($keys) - 1;
386
                }
387
            }
388
389 100
            $isSparceArray = $isObj && (count(array_filter(array_keys($v), 'is_string')) == 0);
390 100
            foreach ($v as $index => $raw) {
391 93
                if ($cx['flags']['spvar']) {
392 83
                    $cx['sp_vars']['first'] = ($i === 0);
393 83
                    $cx['sp_vars']['last'] = ($i == $last);
394 83
                    $cx['sp_vars']['key'] = $index;
395 83
                    $cx['sp_vars']['index'] = $isSparceArray ? $index : $i;
396 83
                    $i++;
397
                }
398 93
                if (isset($bp[0])) {
399 1
                    $raw = static::m($cx, $raw, array($bp[0] => $raw));
400
                }
401 93
                if (isset($bp[1])) {
402 1
                    $raw = static::m($cx, $raw, array($bp[1] => $cx['sp_vars']['index']));
403
                }
404 93
                $ret[] = $cb($cx, $raw);
405
            }
406 99
            if ($cx['flags']['spvar']) {
407 88
                if ($isObj) {
408 14
                    unset($cx['sp_vars']['key']);
409
                } else {
410 76
                    unset($cx['sp_vars']['last']);
411
                }
412 88
                unset($cx['sp_vars']['index']);
413 88
                unset($cx['sp_vars']['first']);
414 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...
415
            }
416 99
            if ($push) {
417 97
                array_pop($cx['scopes']);
418
            }
419 99
            return join('', $ret);
420
        }
421 58
        if ($each) {
422 2
            if ($else !== null) {
423 1
                $ret = $else($cx, $v);
424 1
                return $ret;
425
            }
426 2
            return '';
427
        }
428 57
        if ($isAry) {
429 11
            if ($push) {
430 10
                $cx['scopes'][] = $in;
431
            }
432 11
            $ret = $cb($cx, $v);
433 11
            if ($push) {
434 10
                array_pop($cx['scopes']);
435
            }
436 11
            return $ret;
437
        }
438
439 47
        if ($v === true) {
440 22
            return $cb($cx, $in);
441
        }
442
443 26
        if (($v !== null) && ($v !== false)) {
444 6
            return $cb($cx, $v);
445
        }
446
447 21
        if ($else !== null) {
448 7
            $ret = $else($cx, $in);
449 7
            return $ret;
450
        }
451
452 15
        return '';
453
    }
454
455
    /**
456
     * LightnCandy runtime method for {{#with var}} .
457
     *
458
     * @param array<string,array|string|integer> $cx render time context
459
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
460
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
461
     * @param array<string>|null $bp block parameters
462
     * @param Closure $cb callback function to render child context
463
     * @param Closure|null $else callback function to render child context when {{else}}
464
     *
465
     * @return string The rendered string of the token
466
     *
467
     * @expect '' when input array(), false, null, false, function () {return 'A';}
468
     * @expect '' when input array(), null, null, null, function () {return 'A';}
469
     * @expect '{"a":"b"}' when input array(), array('a'=>'b'), null, array('a'=>'c'), function ($c, $i) {return json_encode($i);}
470
     * @expect '-b=' when input array(), 'b', null, array('a'=>'b'), function ($c, $i) {return "-$i=";}
471
     */
472 22
    public static function wi($cx, $v, $bp, $in, $cb, $else = null) {
473 22
        if (isset($bp[0])) {
474 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...
475
        }
476 22
        if (($v === false) || ($v === null)) {
477 4
            return $else ? $else($cx, $in) : '';
478
        }
479 19
        $cx['scopes'][] = $in;
480 19
        $ret = $cb($cx, $v);
481 19
        array_pop($cx['scopes']);
482 19
        return $ret;
483
    }
484
485
    /**
486
     * LightnCandy runtime method to get merged context
487
     *
488
     * @param array<string,array|string|integer> $cx render time context
489
     * @param array<array|string|integer>|string|integer|null $a the context to be merged
490
     * @param array<array|string|integer>|string|integer|null $b the new context to overwrite
491
     *
492
     * @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...
493
     *
494
     */
495 70
    public static function m($cx, $a, $b) {
496 70
        if (is_array($b)) {
497 70
            if ($a === null) {
498 6
                return $b;
499 64
            } else if (is_array($a)) {
500 61
                return array_merge($a, $b);
501 3
            } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($a)) {
502 1
                foreach ($b as $i => $v) {
503 1
                    $a->$i = $v;
504
                }
505
            }
506
        }
507 3
        return $a;
508
    }
509
510
    /**
511
     * LightnCandy runtime method for {{> partial}} .
512
     *
513
     * @param array<string,array|string|integer> $cx render time context
514
     * @param string $p partial name
515
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
516
     *
517
     * @return string The rendered string of the partial
518
     *
519
     */
520 65
    public static function p($cx, $p, $v, $sp = '') {
521 65
        if (!isset($cx['partials'][$p])) {
522 1
            static::err($cx, "Can not find partial named as '$p' !!");
523
            return '';
524
        }
525
526 64
        return call_user_func($cx['partials'][$p], $cx, static::m($cx, $v[0][0], $v[1]), $sp);
527
    }
528
529
    /**
530
     * LightnCandy runtime method for {{> partial}} .
531
     *
532
     * @param array<string,array|string|integer> $cx render time context
533
     * @param string $p partial name
534
     * @param Closure $code the compiled partial code
535
     *
536
     * @return string The rendered string of the partial
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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.

Loading history...
537
     *
538
     */
539
    public static function in(&$cx, $p, $code) {
540
        $cx['partials'][$p] = $code;
541
    }
542
543
    /**
544 22
     * LightnCandy runtime method for custom helpers.
545 22
     *
546
     * @param array<string,array|string|integer> $cx render time context
547
     * @param string $ch the name of custom helper to be executed
548
     * @param array<array> $vars variables for the 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 array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('-'),array()), 'raw'
554
     * @expect '-&amp;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('&'),array()), 'enc'
555
     * @expect '-&#x27;-' when input array('helpers' => array('a' => function ($i) {return "-$i[0]-";})), 'a', array(array('\''),array()), 'encq'
556
     * @expect '-b-' when input array('helpers' => array('a' => function ($i,$j) {return "-{$j['a']}-";})), 'a', array(array(),array('a' => 'b')), 'raw'
557
     */
558
    public static function ch($cx, $ch, $vars, $op) {
559
        return static::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op);
560
    }
561
562
    /**
563
     * LightnCandy runtime method to handle response of custom helpers.
564
     *
565
     * @param string|array<string,array|string|integer> $ret return value from custom helper
566 165
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
567 165
     *
568 10
     * @return string The rendered string of the token
569 9
     *
570
     * @expect '-&-' when input '-&-', 'raw'
571 10
     * @expect '-&amp;&#039;-' when input '-&\'-', 'enc'
572
     * @expect '-&amp;&#x27;-' when input '-&\'-', 'encq'
573
     * @expect '-&amp;&#039;-' when input array('-&\'-'), 'enc'
574
     * @expect '-&amp;&#x27;-' when input array('-&\'-'), 'encq'
575 165
     * @expect '-&amp;-' when input array('-&-', false), 'enc'
576 9
     * @expect '-&-' when input array('-&-', false), 'raw'
577 99
     * @expect '-&-' when input array('-&-', 'raw'), 'enc'
578 88
     * @expect '-&amp;&#x27;-' when input array('-&\'-', 'encq'), 'raw'
579
     */
580 99
    public static function chret($ret, $op) {
581
        if (is_array($ret)) {
582
            if (isset($ret[1]) && $ret[1]) {
583
                $op = $ret[1];
584
            }
585
            $ret = $ret[0];
586
        }
587
588
        switch ($op) {
589
            case 'enc':
590
                return htmlentities($ret, ENT_QUOTES, 'UTF-8');
591
            case 'encq':
592
                return preg_replace('/=/', '&#x3D;', preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities($ret, ENT_QUOTES, 'UTF-8'))));
593
        }
594
        return $ret;
595
    }
596 145
597 145
    /**
598
     * LightnCandy runtime method for Handlebars.js style custom helpers.
599 145
     *
600 1
     * @param array<string,array|string|integer> $cx render time context
601
     * @param string $ch the name of custom helper to be executed
602
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
603 145
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
604
     * @param boolean $inverted the logic will be inverted
605 145
     * @param Closure|null $cb callback function to render child context
606 145
     * @param Closure|null $else callback function to render child context when {{else}}
607 145
     *
608 145
     * @return string The rendered string of the token
609
     */
610
    public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
611 145
        $isBlock = (is_object($cb) && ($cb instanceof \Closure));
612 58
613
        if (isset($cx['blparam'][0][$ch])) {
614 97
            return $cx['blparam'][0][$ch];
615
        }
616
617 145
        $args = $vars[0];
618 3
        $options = array(
619
            'name' => $ch,
620
            'hash' => $vars[1],
621
            'contexts' => count($cx['scopes']) ? $cx['scopes'] : array(null),
622 145
            'fn.blockParams' => 0,
623 63
        );
624 63
625 63
        if ($isBlock) {
626
            $options['_this'] = &$op;
627
        } else {
628 145
            $options['_this'] = &$inverted;
629
        }
630 46
631 6
        if (isset($vars[2])) {
632
            $options['fn.blockParams'] = count($vars[2]);
633 46
        }
634 6
635 6
        // $invert the logic
636
        if ($inverted) {
637 46
            $tmp = $else;
638 46
            $else = $cb;
639 3
            $cb = $tmp;
640 3
        }
641 44
642 1
        if ($isBlock) {
643
            $options['fn'] = function ($context = '_NO_INPUT_HERE_', $data = null) use ($cx, &$op, $cb, $options, $vars) {
644 46
                if ($cx['flags']['echo']) {
645 27
                    ob_start();
646
                }
647 22
                if (isset($data['data'])) {
648 22
                    $old_spvar = $cx['sp_vars'];
649 22
                    $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $data['data'], array('_parent' => $old_spvar));
650
                }
651 46
                $ex = false;
652 6
                if (isset($data['blockParams']) && isset($vars[2])) {
653
                    $ex = array_combine($vars[2], array_slice($data['blockParams'], 0, count($vars[2])));
654 46
                    array_unshift($cx['blparam'], $ex);
655
                } else if (isset($cx['blparam'][0])) {
656
                    $ex = $cx['blparam'][0];
657
                }
658 145
                if (($context === '_NO_INPUT_HERE_') || ($context === $op)) {
659 7
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $op, $ex) : $op);
660 7
                } else {
661 3
                    $cx['scopes'][] = $op;
662
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $context, $ex) : $context);
663 7
                    array_pop($cx['scopes']);
664 2
                }
665
                if (isset($data['data'])) {
666 5
                    $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...
667 5
                }
668 5
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
669
            };
670 7
        }
671
672
        if ($else) {
673
            $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
674 145
                if ($cx['flags']['echo']) {
675 134
                    ob_start();
676
                }
677
                if ($context === '_NO_INPUT_HERE_') {
678 145
                    $ret = $else($cx, $op);
679 145
                } else {
680 145
                    $cx['scopes'][] = $op;
681
                    $ret = $else($cx, $context);
682
                    array_pop($cx['scopes']);
683 145
                }
684 2
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
685 2
            };
686
        }
687
688 145
        if ($cx['flags']['spvar']) {
689 2
            $options['data'] = $cx['sp_vars'];
690
        }
691
692 144
        $args[] = $options;
693
        $e = null;
694
        $r = true;
695
696
        try {
697
            $r = call_user_func_array($cx['hbhelpers'][$ch], $args);
698
        } catch (\Exception $E) {
699
            $e = "Runtime: call custom helper '$ch' error: " . $E->getMessage();
700
        }
701
702
        if($e !== null) {
703
            static::err($cx, $e);
704
        }
705
706
        return static::chret($r, $isBlock ? 'raw' : $op);
707
    }
708
709
    /**
710
     * LightnCandy runtime method for block custom helpers.
711
     *
712 4
     * @param array<string,array|string|integer> $cx render time context
713 4
     * @param string $ch the name of custom helper to be executed
714
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
715
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
716 4
     * @param boolean $inverted the logic will be inverted
717 2
     * @param Closure $cb callback function to render child context
718 2
     * @param Closure|null $else callback function to render child context when {{else}}
719 2
     *
720
     * @return string The rendered string of the token
721
     *
722 4
     * @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);}
723 4
     * @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);}
724 2
     * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
725 1
     */
726 1
    public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = null) {
727 2
        $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]);
728
729
        // $invert the logic
730 3
        if ($inverted) {
731 3
            $tmp = $else;
732 3
            $else = $cb;
733 3
            $cb = $tmp;
734
        }
735
736
        $ret = '';
737 4
        if ($r === null) {
738
            if ($else) {
739
                $cx['scopes'][] = $in;
740
                $ret = $else($cx, $r);
741
                array_pop($cx['scopes']);
742
            }
743
        } else {
744
            if ($cb) {
745
                $cx['scopes'][] = $in;
746
                $ret = $cb($cx, $r);
747
                array_pop($cx['scopes']);
748
            }
749
        }
750
751
        return $ret;
752
    }
753
}
754
755