GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 202f7f...b25ea6 )
by Zordius
03:45
created

Runtime::p()   B

Complexity

Conditions 7
Paths 36

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 7

Importance

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

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

Loading history...
111
        error_log(var_export($v[0], true));
112
        return '';
113
    }
114
115
    /**
116
     * Resursive lookup variable and helpers. This is slow and will only be used for instance property or method detection or lambdas.
117
     *
118
     * @param array<string,array|string|integer> $cx render time context
119
     * @param array|string|boolean|integer|double|null $in current context
120
     * @param array<array|string|integer> $base current variable context
121
     * @param array<string|integer> $path array of names for path
122
     * @param array|null $args extra arguments for lambda
123
     *
124
     * @return null|string Return the value or null when not found
125
     *
126
     * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), null, 0, array('a', 'b')
127
     * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), null, array('a' => array('b' => 3)), array('a', 'b')
128
     * @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')
129
     * @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')
130
     */
131 385
    public static function v($cx, $in, $base, $path, $args = null) {
132 385
        $count = count($cx['scopes']);
133 385
        $plen = count($path);
134 385
        while ($base) {
135 349
            $v = $base;
136 349
            foreach ($path as $i => $name) {
137 349
                if (is_array($v)) {
138 348
                    if (isset($v[$name])) {
139 344
                        $v = $v[$name];
140 344
                        continue;
141
                    }
142 31
                    if (($i === $plen - 1) && ($name === 'length')) {
143 1
                        return count($v);
144
                    }
145
                }
146 36
                if (is_object($v)) {
147 5
                    if ($cx['flags']['prop'] && !($v instanceof \Closure) && isset($v->$name)) {
148 3
                        $v = $v->$name;
149 3
                        continue;
150
                    }
151 5
                    if ($cx['flags']['method'] && is_callable(array($v, $name))) {
152 4
                        $v = $v->$name();
153 4
                        continue;
154
                    }
155
                }
156 34
                if ($cx['flags']['mustlok']) {
157 31
                    unset($v);
158 31
                    break;
159
                }
160 3
                return null;
161
            }
162 348
            if (isset($v)) {
163 337
                if ($v instanceof \Closure) {
164 29
                    if ($cx['flags']['mustlam'] || $cx['flags']['lambda']) {
165 29
                        if (!$cx['flags']['knohlp'] && ($args || ($args === 0))) {
166 20
                            $A = $args ? $args[0] : array();
167 20
                            $A[] = array('hash' => $args[1], '_this' => $in);
168
                        } else {
169 10
                            $A = array($in);
170
                        }
171 29
                        $v = call_user_func_array($v, $A);
172
                    }
173
                }
174 337
                return $v;
175
            }
176 31
            $count--;
177
            switch ($count) {
178 31
                case -1:
179 30
                    $base = $cx['sp_vars']['root'];
180 30
                    break;
181 30
                case -2:
182 29
                    return null;
183
                default:
184 6
                    $base = $cx['scopes'][$count];
185
            }
186
        }
187 40
        if ($args) {
188 3
            static::err($cx, 'Can not find helper or lambda: "' . implode('.', $path) . '" !');
189
        }
190 40
    }
191
192
    /**
193
     * For {{#if}} .
194
     *
195
     * @param array<string,array|string|integer> $cx render time context
196
     * @param array<array|string|integer>|string|integer|null $v value to be tested
197
     * @param boolean $zero include zero as true
198
     *
199
     * @return boolean Return true when the value is not null nor false.
200
     *
201
     * @expect false when input array(), null, false
202
     * @expect false when input array(), 0, false
203
     * @expect true when input array(), 0, true
204
     * @expect false when input array(), false, false
205
     * @expect true when input array(), true, false
206
     * @expect true when input array(), 1, false
207
     * @expect false when input array(), '', false
208
     * @expect false when input array(), array(), false
209
     * @expect true when input array(), array(''), false
210
     * @expect true when input array(), array(0), false
211
     */
212 74
    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...
213 74
        return ($v !== null) && ($v !== false) && ($zero || ($v !== 0) && ($v !== 0.0)) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
214
    }
215
216
    /**
217
     * For {{^var}} .
218
     *
219
     * @param array<string,array|string|integer> $cx render time context
220
     * @param array<array|string|integer>|string|integer|null $v value to be tested
221
     *
222
     * @return boolean Return true when the value is not null nor false.
223
     *
224
     * @expect true when input array(), null
225
     * @expect false when input array(), 0
226
     * @expect true when input array(), false
227
     * @expect false when input array(), 'false'
228
     * @expect true when input array(), array()
229
     * @expect false when input array(), array('1')
230
     */
231 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...
232 37
        return ($v === null) || ($v === false) || (is_array($v) && (count($v) === 0));
233
    }
234
235
    /**
236
     * For {{var}} .
237
     *
238
     * @param array<string,array|string|integer> $cx render time context
239
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
240
     *
241
     * @return string The htmlencoded value of the specified variable
242
     *
243
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
244
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
245
     * @expect 'a&#039;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
246
     * @expect 'a&b' when input null, new \LightnCandy\SafeString('a&b')
247
     */
248 45
    public static function enc($cx, $var) {
249 45
        if ($var instanceof \LightnCandy\SafeString) {
250 1
            return (string)$var;
251
        }
252
253 45
        return htmlspecialchars(static::raw($cx, $var), ENT_QUOTES, 'UTF-8');
254
    }
255
256
    /**
257
     * For {{var}} , do html encode just like handlebars.js .
258
     *
259
     * @param array<string,array|string|integer> $cx render time context
260
     * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
261
     *
262
     * @return string The htmlencoded value of the specified variable
263
     *
264
     * @expect 'a' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a'
265
     * @expect 'a&amp;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a&b'
266
     * @expect 'a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), 'a\'b'
267
     * @expect '&#x60;a&#x27;b' when input array('flags' => array('mustlam' => 0, 'lambda' => 0)), '`a\'b'
268
     */
269 331
    public static function encq($cx, $var) {
270 331
        if ($var instanceof \LightnCandy\SafeString) {
271 3
            return (string)$var;
272
        }
273
274 328
        return str_replace(array('=', '`', '&#039;'), array('&#x3D;', '&#x60;', '&#x27;'), htmlspecialchars(static::raw($cx, $var), ENT_QUOTES, 'UTF-8'));
275
    }
276
277
    /**
278
     * For {{#var}} or {{#each}} .
279
     *
280
     * @param array<string,array|string|integer> $cx render time context
281
     * @param array<array|string|integer>|string|integer|null $v value for the section
282
     * @param array<string>|null $bp block parameters
283
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
284
     * @param boolean $each true when rendering #each
285
     * @param Closure $cb callback function to render child context
286
     * @param Closure|null $else callback function to render child context when {{else}}
287
     *
288
     * @return string The rendered string of the section
289
     *
290
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), false, null, false, false, function () {return 'A';}
291
     * @expect '' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), null, null, null, false, function () {return 'A';}
292
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), true, null, true, false, function () {return 'A';}
293
     * @expect 'A' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 0, null, 0, false, function () {return 'A';}
294
     * @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=";}
295
     * @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=";}
296
     * @expect '' when input array('scopes' => array(), 'flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'abc', null, 'abc', true, function ($c, $i) {return "-$i=";}
297
     * @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=";}
298
     * @expect '1' when input array('flags' => array('spvar' => 0, 'mustlam' => 0, 'lambda' => 0)), 'b', null, 'b', false, function ($c, $i) {return count($i);}
299
     * @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);}
300
     * @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);}
301
     * @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);}
302
     * @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';}
303
     * @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';}
304
     * @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';}
305
     * @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';}
306
     * @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';}
307
     * @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';}
308
     * @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';}
309
     * @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';}
310
     * @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';}
311
     * @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';}
312
     * @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;}
313
     * @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'];}
314
     */
315 162
    public static function sec($cx, $v, $bp, $in, $each, $cb, $else = null) {
316 162
        $push = ($in !== $v) || $each;
317
318 162
        $isAry = is_array($v) || ($v instanceof \ArrayObject);
319 162
        $isTrav = $v instanceof \Traversable;
320 162
        $loop = $each;
321 162
        $keys = null;
322 162
        $last = null;
323 162
        $isObj = false;
324
325 162
        if ($isAry && $else !== null && count($v) === 0) {
326 3
            $ret = $else($cx, $in);
327 3
            return $ret;
328
        }
329
330
        // #var, detect input type is object or not
331 160
        if (!$loop && $isAry) {
332 64
            $keys = array_keys($v);
333 64
            $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
334 64
            $isObj = !$loop;
335
        }
336
337 160
        if (($loop && $isAry) || $isTrav) {
338 101
            if ($each && !$isTrav) {
339
                // Detect input type is object or not when never done once
340 47
                if ($keys == null) {
341 47
                    $keys = array_keys($v);
342 47
                    $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
343
                }
344
            }
345 101
            $ret = array();
346 101
            if ($push) {
347 99
                $cx['scopes'][] = $in;
348
            }
349 101
            $i = 0;
350 101
            if ($cx['flags']['spvar']) {
351 91
                $old_spvar = $cx['sp_vars'];
352 91
                $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $old_spvar, array('_parent' => $old_spvar));
353 91
                if (!$isTrav) {
354 90
                    $last = count($keys) - 1;
355
                }
356
            }
357
358 101
            $isSparceArray = $isObj && (count(array_filter(array_keys($v), 'is_string')) == 0);
359 101
            foreach ($v as $index => $raw) {
360 94
                if ($cx['flags']['spvar']) {
361 85
                    $cx['sp_vars']['first'] = ($i === 0);
362 85
                    $cx['sp_vars']['last'] = ($i == $last);
363 85
                    $cx['sp_vars']['key'] = $index;
364 85
                    $cx['sp_vars']['index'] = $isSparceArray ? $index : $i;
365 85
                    $i++;
366
                }
367 94
                if (isset($bp[0])) {
368 1
                    $raw = static::m($cx, $raw, array($bp[0] => $raw));
369
                }
370 94
                if (isset($bp[1])) {
371 1
                    $raw = static::m($cx, $raw, array($bp[1] => $cx['sp_vars']['index']));
372
                }
373 94
                $ret[] = $cb($cx, $raw);
374
            }
375 100
            if ($cx['flags']['spvar']) {
376 90
                if ($isObj) {
377 14
                    unset($cx['sp_vars']['key']);
378
                } else {
379 78
                    unset($cx['sp_vars']['last']);
380
                }
381 90
                unset($cx['sp_vars']['index']);
382 90
                unset($cx['sp_vars']['first']);
383 90
                $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...
384
            }
385 100
            if ($push) {
386 98
                array_pop($cx['scopes']);
387
            }
388 100
            return join('', $ret);
389
        }
390 61
        if ($each) {
391 2
            if ($else !== null) {
392 1
                $ret = $else($cx, $v);
393 1
                return $ret;
394
            }
395 2
            return '';
396
        }
397 60
        if ($isAry) {
398 11
            if ($push) {
399 10
                $cx['scopes'][] = $in;
400
            }
401 11
            $ret = $cb($cx, $v);
402 11
            if ($push) {
403 10
                array_pop($cx['scopes']);
404
            }
405 11
            return $ret;
406
        }
407
408 50
        if ($v === true) {
409 22
            return $cb($cx, $in);
410
        }
411
412 29
        if (($v !== null) && ($v !== false)) {
413 6
            return $cb($cx, $v);
414
        }
415
416 24
        if ($else !== null) {
417 10
            $ret = $else($cx, $in);
418 10
            return $ret;
419
        }
420
421 15
        return '';
422
    }
423
424
    /**
425
     * For {{#with}} .
426
     *
427
     * @param array<string,array|string|integer> $cx render time context
428
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
429
     * @param array<array|string|integer>|string|integer|null $in input data with current scope
430
     * @param array<string>|null $bp block parameters
431
     * @param Closure $cb callback function to render child context
432
     * @param Closure|null $else callback function to render child context when {{else}}
433
     *
434
     * @return string The rendered string of the token
435
     *
436
     * @expect '' when input array(), false, null, false, function () {return 'A';}
437
     * @expect '' when input array(), null, null, null, function () {return 'A';}
438
     * @expect '{"a":"b"}' when input array(), array('a'=>'b'), null, array('a'=>'c'), function ($c, $i) {return json_encode($i);}
439
     * @expect '-b=' when input array(), 'b', null, array('a'=>'b'), function ($c, $i) {return "-$i=";}
440
     */
441 28
    public static function wi($cx, $v, $bp, $in, $cb, $else = null) {
442 28
        if (isset($bp[0])) {
443 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...
444
        }
445 28
        if (($v === false) || ($v === null) || (is_array($v) && (count($v) === 0))) {
446 5
            return $else ? $else($cx, $in) : '';
447
        }
448 24
        if ($v === $in) {
449 5
            $ret = $cb($cx, $v);
450
        } else {
451 19
            $cx['scopes'][] = $in;
452 19
            $ret = $cb($cx, $v);
453 19
            array_pop($cx['scopes']);
454
        }
455 24
        return $ret;
456
    }
457
458
    /**
459
     * Get merged context.
460
     *
461
     * @param array<string,array|string|integer> $cx render time context
462
     * @param array<array|string|integer>|string|integer|null $a the context to be merged
463
     * @param array<array|string|integer>|string|integer|null $b the new context to overwrite
464
     *
465
     * @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...
466
     *
467
     */
468 92
    public static function m($cx, $a, $b) {
469 92
        if (is_array($b)) {
470 92
            if ($a === null) {
471 8
                return $b;
472 85
            } else if (is_array($a)) {
473 76
                return array_merge($a, $b);
474 9
            } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($a)) {
475 4
                foreach ($b as $i => $v) {
476 1
                    $a->$i = $v;
477
                }
478
            }
479
        }
480 9
        return $a;
481
    }
482
483
    /**
484
     * For {{> partial}} .
485
     *
486
     * @param array<string,array|string|integer> $cx render time context
487
     * @param string $p partial name
488
     * @param array<array|string|integer>|string|integer|null $v value to be the new context
489
     *
490
     * @return string The rendered string of the partial
491
     *
492
     */
493 88
    public static function p($cx, $p, $v, $pid, $sp = '') {
494 88
        $pp = ($p === '@partial-block') ? "$p" . ($pid > 0 ? $pid : $cx['partialid']) : $p;
495
496 88
        if (!isset($cx['partials'][$pp])) {
497 2
            static::err($cx, "Can not find partial named as '$pp' !!");
498 1
            return '';
499
        }
500
501 86
        $cx['partialid'] = ($p === '@partial-block') ? (($pid > 0) ? $pid : (($cx['partialid'] > 0) ? $cx['partialid'] - 1 : 0)) : $pid;
502
503 86
        return call_user_func($cx['partials'][$pp], $cx, static::m($cx, $v[0][0], $v[1]), $sp);
504
    }
505
506
    /**
507
     * For {{#* inlinepartial}} .
508
     *
509
     * @param array<string,array|string|integer> $cx render time context
510
     * @param string $p partial name
511
     * @param Closure $code the compiled partial code
512
     *
513
     */
514 7
    public static function in(&$cx, $p, $code) {
515 7
        $cx['partials'][$p] = $code;
516 7
    }
517
518
    /**
519
     * For custom helpers.
520
     *
521
     * @param array<string,array|string|integer> $cx render time context
522
     * @param string $ch the name of custom helper to be executed
523
     * @param array<array|string|integer>|string|integer|null $vars variables for the helper
524
     * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
525
     * @param boolean $inverted the logic will be inverted
526
     * @param Closure|null $cb callback function to render child context
527
     * @param Closure|null $else callback function to render child context when {{else}}
528
     *
529
     * @return string The rendered string of the token
530
     */
531 163
    public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
532 163
        $isBlock = (is_object($cb) && ($cb instanceof \Closure));
533
534 163
        if (isset($cx['blparam'][0][$ch])) {
535 1
            return $cx['blparam'][0][$ch];
536
        }
537
538 163
        $args = $vars[0];
539
        $options = array(
540 163
            'name' => $ch,
541 163
            'hash' => $vars[1],
542 163
            'contexts' => count($cx['scopes']) ? $cx['scopes'] : array(null),
543 163
            'fn.blockParams' => 0,
544
        );
545
546 163
        if ($isBlock) {
547 56
            $options['_this'] = &$op;
548
        } else {
549 117
            $options['_this'] = &$inverted;
550
        }
551
552 163
        if (isset($vars[2])) {
553 3
            $options['fn.blockParams'] = count($vars[2]);
554
        }
555
556
        // $invert the logic
557 163
        if ($inverted) {
558 75
            $tmp = $else;
559 75
            $else = $cb;
560 75
            $cb = $tmp;
561
        }
562
563 163
        if ($isBlock) {
564
            $options['fn'] = function ($context = '_NO_INPUT_HERE_', $data = null) use ($cx, &$op, $cb, $options, $vars) {
565 45
                if ($cx['flags']['echo']) {
566
                    ob_start();
567
                }
568 45
                if (isset($data['data'])) {
569 6
                    $old_spvar = $cx['sp_vars'];
570 6
                    $cx['sp_vars'] = array_merge(array('root' => $old_spvar['root']), $data['data'], array('_parent' => $old_spvar));
571
                }
572 45
                $ex = false;
573 45
                if (isset($data['blockParams']) && isset($vars[2])) {
574 3
                    $ex = array_combine($vars[2], array_slice($data['blockParams'], 0, count($vars[2])));
575 3
                    array_unshift($cx['blparam'], $ex);
576 43
                } else if (isset($cx['blparam'][0])) {
577 1
                    $ex = $cx['blparam'][0];
578
                }
579 45
                if (($context === '_NO_INPUT_HERE_') || ($context === $op)) {
580 28
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $op, $ex) : $op);
581
                } else {
582 20
                    $cx['scopes'][] = $op;
583 20
                    $ret = $cb($cx, is_array($ex) ? static::m($cx, $context, $ex) : $context);
584 20
                    array_pop($cx['scopes']);
585
                }
586 45
                if (isset($data['data'])) {
587 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...
588
                }
589 45
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
590
            };
591
        }
592
593 163
        if ($else) {
594
            $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
595 6
                if ($cx['flags']['echo']) {
596
                    ob_start();
597
                }
598 6
                if ($context === '_NO_INPUT_HERE_') {
599 1
                    $ret = $else($cx, $op);
600
                } else {
601 5
                    $cx['scopes'][] = $op;
602 5
                    $ret = $else($cx, $context);
603 5
                    array_pop($cx['scopes']);
604
                }
605 6
                return $cx['flags']['echo'] ? ob_get_clean() : $ret;
606 10
            };
607
        } else {
608
            $options['inverse'] = function () {
609
                return '';
610
            };
611
        }
612
613 163
        if ($cx['flags']['spvar']) {
614 154
            $options['data'] = $cx['sp_vars'];
615
        }
616
617 163
        $args[] = $options;
618 163
        $e = null;
619 163
        $r = true;
620
621
        try {
622 163
            $r = call_user_func_array($cx['helpers'][$ch], $args);
623 2
        } catch (\Exception $E) {
624 2
            $e = "Runtime: call custom helper '$ch' error: " . $E->getMessage();
625
        }
626
627 163
        if($e !== null) {
628 2
            static::err($cx, $e);
629
        }
630
631 162
        return $r;
632
    }
633
}
634
635