File Coverage

lib/PHP/Decode/Func.pm
Criterion Covered Total %
statement 1035 1542 67.1
branch 598 1244 48.0
condition 102 318 32.0
subroutine 43 45 95.5
pod 1 18 5.5
total 1779 3167 56.1


line stmt bran cond sub pod time code
1             #
2             # execute PHP buildin functions
3             #
4             package PHP::Decode::Func;
5              
6 4     4   28 use strict;
  4         11  
  4         131  
7 4     4   36 use warnings;
  4         9  
  4         134  
8 4     4   24 use List::Util qw(min max shuffle);
  4         11  
  4         441  
9 4     4   1903 use MIME::Base64;
  4         2872  
  4         231  
10 4     4   2334 use Compress::Zlib;
  4         248376  
  4         927  
11 4     4   41 use Digest::MD5 qw(md5 md5_hex);
  4         11  
  4         328  
12 4     4   1956 eval "use Digest::SHA1 qw(sha1 sha1_hex); 1" or eval "use Digest::SHA qw(sha1 sha1_hex); 1" or die "Digest::SHA/SHA1 required";
  4         2834  
  4         194  
13 4     4   2103 use HTML::Entities;
  4         23460  
  4         304  
14 4     4   1809 use URI::Escape;
  4         5374  
  4         240  
15 4     4   31 use File::Basename;
  4         9  
  4         355  
16 4     4   35 use PHP::Decode::Array qw(is_int_index);
  4         10  
  4         209  
17 4     4   1971 use PHP::Decode::Op;
  4         10  
  4         145  
18 4     4   27 use PHP::Decode::Parser qw(:all);
  4         32  
  4         575  
19 4     4   37 use PHP::Decode::Transformer;
  4         17  
  4         4348  
20              
21             my $VERSION = '0.129';
22              
23             # if block contains just a sequence of strings, then return joined string
24             #
25             sub _joinable_str {
26 6     6   13 my ($parser, $s, $val) = @_;
27              
28 6 100       25 if (is_block($s)) {
29 1         3 my ($type, $a) = @{$parser->{strmap}{$s}};
  1         4  
30 1         5 foreach my $stmt (@$a) {
31 1         6 $val = &_joinable_str($parser, $stmt, $val);
32 1 50       7 last if (!defined $val);
33             }
34 1         3 return $val;
35             }
36 5 100       15 if (is_strval($s)) {
37 4         16 return $val . $parser->{strmap}{$s};
38             }
39 1 50       22 if (is_null($s)) {
40 0         0 return '';
41             }
42 1         7 return;
43             }
44              
45             sub parsing_func {
46 3     3 0 8 my ($ctx) = @_;
47              
48 3 100       12 if ($ctx->{infunction}) {
49 1 50       4 if (!$ctx->{incall}) {
50 1         10 return 0;
51             }
52 0         0 return 1;
53             }
54 2         14 return 0;
55             }
56              
57             # see: https://gist.github.com/BaiGang/1321793
58             #
59             sub levenshtein {
60 0     0 0 0 my ($str1, $str2) = @_;
61 0         0 my ($len1, $len2) = (length $str1, length $str2);
62 0         0 my %mat;
63            
64 0 0       0 if ($len1 == 0) {
65 0         0 return $len2;
66             }
67 0 0       0 if ($len2 == 0) {
68 0         0 return $len1;
69             }
70 0         0 for (my $i = 0; $i <= $len1; ++$i) {
71 0         0 $mat{0}{$i} = $i;
72 0         0 $mat{1}{$i} = 0;
73             }
74 0         0 my @ar1 = split //, $str1;
75 0         0 my @ar2 = split //, $str2;
76            
77 0         0 for (my $j = 1; $j <= $len2; ++$j) {
78 0         0 my $p = $j % 2;
79 0         0 my $q = ($j + 1) % 2;
80 0         0 $mat{$p}{0} = $j;
81 0         0 for (my $i = 1; $i <= $len1; ++$i) {
82 0         0 my $cost = 0;
83 0 0       0 if ($ar1[$i-1] ne $ar2[$j-1]) {
84 0         0 $cost = 1;
85             }
86             $mat{$p}{$i} = min($cost + $mat{$q}{$i-1},
87 0         0 $mat{$p}{$i-1} + 1, $mat{$q}{$i} + 1);
88             }
89             }
90 0         0 return $mat{$len2%2}{$len1};
91             }
92              
93             # see: Perl-only CRC32 function
94             # http://billauer.co.il/blog/2011/05/perl-crc32-crc-xs-module/
95             #
96             my $crc32a_table;
97             my $crc32b_table;
98              
99             sub mycrc32a {
100 1     1 0 4 my ($input, $init_value, $polynomial) = @_;
101              
102             # hash('crc32', $str);
103             # == perl -I./Digest-CRC-0.22/lib -e 'use Digest::CRC;
104             # $ctx = Digest::CRC->new(width=>32, init=>0xffffffff, xorout=>0xffffffff, refout=>0, poly=>0x4C11DB7, refin=>0, cont=>0);
105             # $ctx->add("test");
106             # print $digest = $ctx->hexdigest . "\n";' --> (need to read bytes backwards)
107             #
108 1 50       5 $init_value = 0 unless (defined $init_value);
109 1 50       4 $polynomial = 0x04c11db7 unless (defined $polynomial);
110              
111 1 50       4 unless (defined $crc32a_table) {
112 1         3 $crc32a_table = [];
113              
114 1         5 for (my $i=0; $i<256; $i++) {
115 256         306 my $x = $i << 24;
116 256         415 for (my $j=0; $j<8; $j++) {
117 2048 100       2958 if ($x & (1 << 31)) {
118 1024         1742 $x = ($x << 1) ^ $polynomial;
119             } else {
120 1024         1664 $x = $x << 1;
121             }
122             }
123 256         332 $x = $x & 0xffffffff;
124 256         462 push @$crc32a_table, $x;
125             }
126             }
127 1         3 my $crc = $init_value ^ 0xffffffff;
128              
129 1         13 foreach my $x (unpack ('C*', $input)) {
130 4         12 $crc = (($crc << 8) & 0xffffff00) ^ $crc32a_table->[(($crc >> 24) & 0xff) ^ ($x & 0xff)];
131             }
132 1         4 $crc = $crc ^ 0xffffffff;
133              
134             # reverse byteorder
135 1         3 $crc = ($crc << 24) & 0xff000000 | ($crc << 8) & 0xff0000 | ($crc >> 8) & 0xff00 | ($crc >> 24) & 0xff;
136 1         6 return $crc;
137             }
138              
139             sub mycrc32b {
140 3     3 0 8 my ($input, $init_value, $polynomial) = @_;
141              
142             # hash('crc32b', $str);
143             # == perl -I./Digest-CRC-0.22/lib -e 'use Digest::CRC;
144             # $ctx = Digest::CRC->new(width=>32, init=>0xffffffff, xorout=>0xffffffff, refout=>1, poly=>0x4C11DB7, refin=>1, cont=>0);
145             # $ctx->add("test");
146             # print $digest = $ctx->hexdigest;'
147             #
148 3 50       11 $init_value = 0 unless (defined $init_value);
149             #$polynomial = _reflect(0x04c11db7,32) unless (defined $polynomial);
150 3 50       8 $polynomial = 0xedb88320 unless (defined $polynomial);
151              
152 3 100       19 unless (defined $crc32b_table) {
153 1         3 $crc32b_table = [];
154 1         4 for (my $i=0; $i<256; $i++) {
155 256         303 my $x = $i;
156 256         442 for (my $j=0; $j<8; $j++) {
157 2048 100       3029 if ($x & 1) {
158 1024         1758 $x = ($x >> 1) ^ $polynomial;
159             } else {
160 1024         1711 $x = $x >> 1;
161             }
162             }
163 256         443 push @$crc32b_table, $x;
164             }
165             }
166 3         10 my $crc = $init_value ^ 0xffffffff;
167              
168 3         27 foreach my $x (unpack ('C*', $input)) {
169 12         30 $crc = (($crc >> 8) & 0xffffff) ^ $crc32b_table->[($crc ^ $x) & 0xff];
170             }
171 3         8 $crc = $crc ^ 0xffffffff;
172 3         12 return $crc;
173             }
174              
175             # see: https://metacpan.org/dist/PHP-Strings/source/Strings.pm
176             # todo: might use more functions from PHP::Strings module
177             #
178             sub stripcslashes {
179 0     0 0 0 my ($str) = @_;
180              
181 0         0 $str =~ s{
182             \\([abfnrtv\\?'"])
183             |
184             \\(\d\d\d)
185             |
186             \\(x[[:xdigit:]]{2})
187             |
188             \\(x[[:xdigit:]])
189             }{
190 0 0       0 if ( $+ eq 'v' ) {
    0          
191 0         0 "\013";
192             } elsif (length $+ == 1) {
193 0         0 eval qq{qq/\\$+/};
194             } else {
195 0         0 chr oct "0$+";
196             }
197             }exg ;
198 0         0 return $str;
199             }
200              
201             sub dyn_replace {
202 13     13 0 25 my ($replace) = @_;
203 13         21 my @groups;
204             {
205 4     4   45 no strict 'refs';
  4         15  
  4         5957  
  13         17  
206 13         62 $groups[$_] = $$_ for 1 .. $#-; # the size of @- tells us the number of capturing groups
207             }
208             # For the e modifier preg_replace escapes [' " \ NULL] in the replacement string.
209             # see: https://www.php.net/manual/en/function.preg-replace.php
210             #
211 13         38 for (my $i=1; $i < scalar @groups; $i++) {
212             #print ">> dyn_replace $i: $groups[$i]\n";
213 7         28 $groups[$i] =~ s/(["'\\\0])/\\$1/g;
214             }
215 13         47 $replace =~ s/\$(\d+)/$groups[$1]/g;
216 13         41 return $replace;
217             }
218              
219             sub dyn_replace_eval {
220 5     5 0 13 my ($ctx, $replace) = @_;
221 5         10 my $parser = $ctx->{parser};
222 5         10 my $res = dyn_replace($replace);
223              
224 5         31 my $code = $parser->setstr($res);
225 5         17 my $parser2 = $parser->subparser();
226 5         23 my $ctx2 = $ctx->subctx(parser => $parser2);
227 5         12 my $block = $ctx2->parse_eval($code);
228 5         20 my $k = $ctx2->exec_eval($block);
229 5         27 my $str = _joinable_str($parser, $k, '');
230 5 100       13 unless (defined $str) {
231             # mark non resolvable code with {{{#stmtX}}} pattern
232             #
233 1         5 $str = '{{{' . $k . '}}}';
234             }
235 5 50       16 $ctx->{log}->($ctx, 'replace', $replace, "$res -> eval($code) -> $str ($k)") if $ctx->{log};
236 5         32 return $str;
237             }
238              
239             sub dyn_result {
240 4     4 0 11 my ($str, $parser) = @_;
241 4         11 my @out;
242             my $k;
243 4         0 my $res;
244              
245             # when the result contains non resolved statements,
246             # then create a chain of concatted expresions.
247             #
248 4         16 while ($str =~ /^(.*?)\{\{\{(#\w+\d+)\}\}\}(.*)$/) {
249 1 50       6 if ($1 ne '') {
250 0         0 $k = $parser->setstr($1);
251 0         0 push(@out, $k);
252             }
253 1         4 push(@out, $2);
254 1         6 $str = $3;
255             }
256 4 100 66     30 if (($str eq '') && (scalar @out > 0)) {
257 1         5 $res = pop(@out);
258             } else {
259 3         11 $res = $parser->setstr($str);
260             }
261 4         14 while (scalar @out > 0) {
262 0         0 my $op1 = pop(@out);
263 0         0 $res = $parser->setexpr('.', $op1, $res);
264             }
265 4         11 return $res;
266             }
267              
268             sub preg_replace {
269 13     13 0 30 my ($ctx, $_pattern, $_replacement, $str, $mode, $limit) = @_;
270 13         22 my $parser = $ctx->{parser};
271 13         34 my $pattern = $parser->get_strval($_pattern);
272 13         43 my $replacement = $parser->get_strval($_replacement);
273 13         21 my $cnt = 0;
274 13         20 my $res;
275             my $k;
276              
277 13 100       27 if ($str eq '#null') {
278 1         3 $res = '';
279             } else {
280 12         25 $res = $parser->get_strval($str);
281             }
282 13 50 33     63 unless (defined $pattern && defined $res) {
283 0         0 return;
284             }
285 13         19 my $m = '';
286 13         18 my $modifier;
287 13 100       27 if ($mode eq 'preg') {
288 8         26 my ($delim) = $pattern =~ /^(.)/;
289 8         14 my $delim2;
290              
291             # allow bracket delimiters
292             # https://php.net/regexp.reference.delimiters
293             #
294 8 50       34 if ($delim eq '[') {
    50          
    100          
    50          
295 0         0 $delim2 = ']';
296             } elsif ($delim eq '(') {
297 0         0 $delim2 = ')';
298             } elsif ($delim eq '{') {
299 1         3 $delim2 = '}';
300             } elsif ($delim eq '<') {
301 0         0 $delim2 = '>';
302             } else {
303 7         9 $delim2 = $delim;;
304             }
305             # TODO: need to handle escapes in pattern?
306             # https://php.net/manual/en/function.preg-quote.php
307             #
308 8 50       143 if ($pattern =~ m|^\Q$delim\E([^$delim2]*)\Q$delim2\E(\w*)$|) {
309 8         27 $pattern = $1;
310 8         34 $modifier = $2;
311 8 100       24 $m .= 'i' if ($modifier =~ /i/);
312 8 50       19 $m .= 'm' if ($modifier =~ /m/);
313 8 50       14 $m .= 's' if ($modifier =~ /s/);
314 8 50       22 $m .= 'x' if ($modifier =~ /x/);
315             }
316             }
317              
318             # don't escape pattern here - it might contain regexp rules
319             # don't escape replacement here - special-chars should not get excaped
320             #
321 13 50       26 if ($limit == 0) {
322             # delete pattern
323 0 0       0 if ($mode eq 'preg') {
324 0         0 $res =~ s/$pattern//g;
325             } else {
326 0         0 $res =~ s/\Q$pattern\E//g;
327             }
328 0         0 $k = $parser->setstr($res);
329             } else {
330 13 50       25 unless (defined $replacement) {
331 0         0 return;
332             }
333              
334             # the replacement might contain backreferences in the
335             # form '\1'. Convert them to '$1' for perl call.
336             #
337 13 100       27 if ($mode eq 'preg') {
338 8         33 $replacement =~ s/(\\([0-9]))/\$$2/g;
339             }
340              
341             # When the replacement contains backreferences and
342             # it comes from data, it needs to get evaled first.
343             # (eval as string by /ee does not help in this case).
344             #
345             # http://stackoverflow.com/questions/1908159/perl-can-i-store-backreferences-not-their-values-in-variables
346             #
347 13 100 100     51 if (defined $modifier && $modifier =~ /e/) {
348             # eval-modifier deprecated since php-5.5
349             # https://php.net/manual/en/reference.pcre.pattern.modifiers.php#reference.pcre.pattern.modifiers.eval
350             #
351 4         9 eval { $res =~ s/$pattern/dyn_replace_eval($ctx, $replacement)/eg; };
  4         53  
  5         20  
352 4 50       15 if ($@) {
353 0         0 $ctx->{warn}->($ctx, 'replace', $str, "bad preg/e $pattern");
354 0         0 return;
355             }
356 4 50       17 if (scalar @- > 0) {
357 4         7 $cnt++;
358             }
359 4         14 $k = dyn_result($res, $parser);
360             } else {
361 9 50       20 if ($limit != -1) {
362 0         0 while ($cnt < $limit) {
363 0 0       0 if ($mode eq 'preg') {
364 0         0 eval { $res =~ s/$pattern/dyn_replace($replacement)/e; };
  0         0  
  0         0  
365             } else {
366 0         0 eval { $res =~ s/\Q$pattern\E/$replacement/; };
  0         0  
367             }
368 0 0       0 if ($@) {
369 0         0 $ctx->{warn}->($ctx, 'replace', $str, "bad preg $pattern");
370 0         0 return;
371             }
372 0 0       0 if (scalar @- == 0) {
373             # no match found in substitution
374 0         0 last;
375             }
376 0         0 $cnt++;
377             }
378             } else {
379 9 100       18 if ($mode eq 'preg') {
380 4         7 eval { $res =~ s/$pattern/dyn_replace($replacement)/eg; };
  4         40  
  8         29  
381             } else {
382             # TODO: use quotemeta($replacement) here?
383             #
384 5         8 eval { $res =~ s/\Q$pattern\E/$replacement/g; };
  5         66  
385             }
386 9 50       56 if ($@) {
387 0         0 $ctx->{warn}->($ctx, 'replace', $str, "bad preg $pattern");
388 0         0 return;
389             }
390 9 50       27 if (scalar @- > 0) {
391 9         13 $cnt++;
392             }
393             }
394 9         31 $k = $parser->setstr($res);
395             }
396             }
397 13 50       29 $ctx->{log}->($ctx, 'replace', $str, "[%s]->[%s] %s -> %s", $parser->shortstr($pattern,40), $parser->shortstr($replacement,40), $parser->shortstr($str,40), $parser->shortstr($res,40)) if $ctx->{log};
398 13         41 return ($k, $cnt);
399             }
400              
401             sub preg_replace_subject {
402 13     13 0 34 my ($ctx, $pattern, $replacement, $str, $mode, $limit) = @_;
403 13         24 my $parser = $ctx->{parser};
404              
405 13 100       31 if (is_array($pattern)) {
    50          
406 1         3 my $arr = $parser->{strmap}{$pattern};
407 1         5 my $keys = $arr->get_keys();
408 1         3 my $rep = $replacement;
409 1         3 my $r_arr;
410             my $r_keys;
411 1         2 my $cnt = 0;
412              
413 1 50       4 if (is_array($replacement)) {
414 1         7 $r_arr = $parser->{strmap}{$replacement};
415 1         4 $r_keys = $r_arr->get_keys();
416             }
417 1         11 foreach my $k (@$keys) {
418 1         4 my $val = $arr->val($k);
419 1 50       4 if (is_array($replacement)) {
420 1         3 my $rk = shift(@$r_keys);
421 1         4 $rep = $r_arr->val($rk);
422             }
423             #$ctx->{log}->($ctx, 'replace', $str, "preg $val -> $rep [$str]") if $ctx->{log};
424 1         3 my ($r, $n) = preg_replace($ctx, $val, $rep, $str, $mode, $limit);
425 1 50       4 if ($limit != -1) {
426 0         0 $limit -= $cnt;
427             }
428 1         4 $cnt += $n;
429 1         2 $str = $r;
430             }
431 1         5 return ($str, $cnt);
432             } elsif (is_strval($pattern)) {
433 12         29 my ($r, $cnt) = preg_replace($ctx, $pattern, $replacement, $str, $mode, $limit);
434 12         40 return ($r, $cnt);
435             }
436 0         0 return;
437             }
438              
439 4     4   35 use constant R_VOID => 0x0001;
  4         12  
  4         381  
440 4     4   28 use constant R_INT => 0x0002;
  4         8  
  4         235  
441 4     4   75 use constant R_FLOAT => 0x0004;
  4         23  
  4         240  
442 4     4   27 use constant R_BOOL => 0x0008;
  4         8  
  4         240  
443 4     4   24 use constant R_STR => 0x0010;
  4         8  
  4         224  
444 4     4   27 use constant R_ARRAY => 0x0020;
  4         13  
  4         206  
445 4     4   27 use constant R_OBJECT => 0x0040;
  4         8  
  4         195  
446 4     4   45 use constant R_CALL => 0x0080;
  4         10  
  4         209  
447 4     4   31 use constant R_MIXED => 0x0100;
  4         10  
  4         181  
448 4     4   24 use constant R_FIX => 0x0200; # string not usable by eval
  4         7  
  4         212  
449 4     4   23 use constant R_FIXSTR => (R_FIX|R_STR);
  4         9  
  4         85269  
450              
451             # https://php.net/manual/en/function.get-defined-functions.php
452             # list all: php -r '$x = get_defined_functions(); print_r($x);' | less
453             # list standard: php -r '$f=get_extension_funcs("standard"); print_r($f);' | less
454             # list per extension: php -r '$x = get_loaded_extensions(); foreach ($x as $e) { print ("$e: \n"); $f=get_extension_funcs($e); print_r($f); }' | less
455             #
456             my %php_funcs_core = (
457             zend_version => { ret => R_FIXSTR },
458             func_num_args => { ret => R_INT },
459             func_get_arg => { ret => R_MIXED },
460             func_get_args => { ret => R_ARRAY },
461             strlen => { ret => R_INT },
462             strcmp => { ret => R_INT },
463             strncmp => { ret => R_INT },
464             strcasecmp => { ret => R_INT },
465             strncasecmp => { ret => R_INT },
466             each => { param => ['#ref0'], ret => R_ARRAY },
467             error_reporting => { ret => R_INT },
468             define => { ret => R_BOOL },
469             defined => { ret => R_BOOL },
470             get_class => { ret => R_STR },
471             get_called_class => { ret => R_STR },
472             get_parent_class => { ret => R_STR },
473             method_exists => { ret => R_BOOL },
474             property_exists => { ret => R_BOOL },
475             class_exists => { ret => R_BOOL },
476             interface_exists => { ret => R_BOOL },
477             function_exists => { ret => R_BOOL },
478             class_alias => { ret => R_BOOL },
479             get_included_files => { ret => R_ARRAY },
480             get_required_files => { ret => R_ARRAY },
481             is_subclass_of => { ret => R_BOOL },
482             is_a => { ret => R_BOOL },
483             get_class_vars => { ret => R_ARRAY },
484             get_object_vars => { ret => R_ARRAY },
485             get_class_methods => { ret => R_ARRAY },
486             trigger_error => { ret => R_BOOL },
487             user_error => { ret => R_BOOL },
488             set_error_handler => { ret => R_CALL },
489             restore_error_handler => { ret => R_BOOL },
490             set_exception_handler => { ret => R_CALL },
491             restore_exception_handler => { ret => R_BOOL },
492             get_declared_classes => { ret => R_ARRAY },
493             get_declared_interfaces => { ret => R_ARRAY },
494             get_defined_functions => { ret => R_ARRAY },
495             get_defined_vars => { ret => R_ARRAY },
496             create_function => { ret => R_STR, callable => 1 },
497             get_resource_type => { ret => R_FIXSTR },
498             get_loaded_extensions => { ret => R_ARRAY },
499             extension_loaded => { ret => R_BOOL },
500             get_extension_funcs => { ret => R_ARRAY },
501             get_defined_constants => { ret => R_ARRAY },
502             debug_backtrace => { ret => R_ARRAY },
503             debug_print_backtrace => { ret => R_VOID },
504             gc_collect_cycles => { ret => R_INT },
505             gc_enabled => { ret => R_BOOL },
506             gc_enable => { ret => R_VOID },
507             gc_disable => { ret => R_VOID },
508             gc_status => { ret => R_ARRAY },
509             );
510              
511             my %php_funcs_standard = (
512             exit => { ret => R_VOID }, # is language construct
513             die => { ret => R_VOID }, # is language construct
514             unset => { ret => R_VOID }, # is language construct
515             constant => { ret => R_MIXED },
516             bin2hex => { ret => R_FIXSTR },
517             sleep => { ret => R_INT },
518             usleep => { ret => R_VOID },
519             time_nanosleep => { ret => R_ARRAY|R_BOOL },
520             time_sleep_until => { ret => R_BOOL },
521             strptime => { ret => R_STR },
522             flush => { ret => R_VOID },
523             wordwrap => { ret => R_STR },
524             htmlspecialchars => { ret => R_STR },
525             htmlentities => { ret => R_STR },
526             html_entity_decode => { ret => R_STR },
527             htmlspecialchars_decode => { ret => R_STR },
528             get_html_translation_table => { ret => R_ARRAY },
529             sha1 => { ret => R_FIXSTR },
530             sha1_file => { ret => R_FIXSTR|R_BOOL },
531             md5 => { ret => R_FIXSTR },
532             md5_file => { ret => R_FIXSTR|R_BOOL },
533             crc32 => { ret => R_INT },
534             iptcparse => {},
535             iptcembed => {},
536             getimagesize => {},
537             image_type_to_mime_type => {},
538             image_type_to_extension => {},
539             phpinfo => {},
540             phpversion => {},
541             phpcredits => {},
542             php_logo_guid => {},
543             php_real_logo_guid => {},
544             php_egg_logo_guid => {},
545             zend_logo_guid => {},
546             php_sapi_name => {},
547             php_uname => { ret => R_STR },
548             php_ini_scanned_files => {},
549             php_ini_loaded_file => {},
550             strnatcmp => {},
551             strnatcasecmp => {},
552             substr_count => { ret => R_INT },
553             strspn => {},
554             strcspn => {},
555             strtok => {},
556             strtoupper => {},
557             strtolower => {},
558             strpos => {},
559             stripos => {},
560             strrpos => {},
561             strripos => {},
562             strrev => { ret => R_STR },
563             hebrev => {},
564             hebrevc => {},
565             nl2br => {},
566             basename => {},
567             dirname => {},
568             pathinfo => {},
569             stripslashes => {},
570             stripcslashes => {},
571             strstr => {},
572             stristr => {},
573             strrchr => {},
574             str_shuffle => {},
575             str_word_count => {},
576             str_split => { ret => R_ARRAY },
577             strpbrk => {},
578             substr_compare => {},
579             strcoll => {},
580             money_format => {},
581             substr => {},
582             substr_replace => {},
583             quotemeta => {},
584             ucfirst => {},
585             lcfirst => {},
586             ucwords => {},
587             strtr => {},
588             addslashes => {},
589             addcslashes => {},
590             rtrim => { ret => R_STR },
591             str_replace => {},
592             str_ireplace => {},
593             str_repeat => {},
594             count_chars => {},
595             chunk_split => {},
596             trim => { ret => R_STR },
597             ltrim => { ret => R_STR },
598             strip_tags => {},
599             similar_text => {},
600             explode => {},
601             implode => {},
602             join => {},
603             setlocale => {},
604             localeconv => {},
605             nl_langinfo => {},
606             soundex => {},
607             levenshtein => {},
608             chr => {},
609             ord => {},
610             parse_str => {},
611             str_getcsv => {},
612             str_pad => {},
613             chop => {},
614             strchr => {},
615             sprintf => {},
616             printf => {},
617             vprintf => {},
618             vsprintf => {},
619             fprintf => {},
620             vfprintf => {},
621             sscanf => {},
622             fscanf => {},
623             parse_url => {},
624             urlencode => { ret => R_STR },
625             urldecode => { ret => R_STR },
626             rawurlencode => { ret => R_STR },
627             rawurldecode => { ret => R_STR },
628             http_build_query => {},
629             readlink => {},
630             linkinfo => {},
631             symlink => {},
632             link => {},
633             unlink => {},
634             exec => { param => ['#str0', '#ref0', '#ref0'], ret => R_STR|R_BOOL },
635             system => {},
636             escapeshellcmd => {},
637             escapeshellarg => {},
638             passthru => {},
639             shell_exec => {},
640             proc_open => {},
641             proc_close => {},
642             proc_terminate => {},
643             proc_get_status => {},
644             proc_nice => {},
645             rand => {},
646             srand => {},
647             getrandmax => {},
648             mt_rand => {},
649             mt_srand => {},
650             mt_getrandmax => {},
651             getservbyname => {},
652             getservbyport => {},
653             getprotobyname => {},
654             getprotobynumber => {},
655             getmyuid => {},
656             getmygid => {},
657             getmypid => {},
658             getmyinode => {},
659             getlastmod => {},
660             base64_decode => { cmd => \&cmd_base64_decode, ret => R_STR },
661             base64_encode => { cmd => \&cmd_base64_encode, ret => R_STR },
662             convert_uuencode => {},
663             convert_uudecode => {},
664             abs => {},
665             ceil => {},
666             floor => {},
667             round => {},
668             sin => {},
669             cos => {},
670             tan => {},
671             asin => {},
672             acos => {},
673             atan => {},
674             atanh => {},
675             atan2 => {},
676             sinh => {},
677             cosh => {},
678             tanh => {},
679             asinh => {},
680             acosh => {},
681             expm1 => {},
682             log1p => {},
683             pi => {},
684             is_finite => {},
685             is_nan => {},
686             is_infinite => {},
687             pow => { ret => R_INT|R_FLOAT },
688             exp => {},
689             log => {},
690             log10 => {},
691             sqrt => {},
692             hypot => {},
693             deg2rad => {},
694             rad2deg => {},
695             bindec => {},
696             hexdec => {},
697             octdec => {},
698             decbin => {},
699             decoct => {},
700             dechex => {},
701             base_convert => {},
702             number_format => {},
703             fmod => {},
704             inet_ntop => {},
705             inet_pton => {},
706             ip2long => {},
707             long2ip => {},
708             getenv => { cmd => \&PHP::Decode::Transformer::cmd_getenv, ret => R_STR },
709             putenv => {},
710             getopt => {},
711             sys_getloadavg => {},
712             microtime => {},
713             gettimeofday => {},
714             getrusage => {},
715             uniqid => {},
716             quoted_printable_decode => {},
717             quoted_printable_encode => {},
718             convert_cyr_string => {},
719             get_current_user => {},
720             set_time_limit => {},
721             get_cfg_var => {},
722             magic_quotes_runtime => {},
723             set_magic_quotes_runtime => {},
724             get_magic_quotes_gpc => {},
725             get_magic_quotes_runtime => {},
726             import_request_variables => {},
727             error_log => {},
728             error_get_last => {},
729             call_user_func => { callable => 1 },
730             call_user_func_array => { callable => 1 },
731             call_user_method => { callable => 1 },
732             call_user_method_array => { callable => 1 },
733             forward_static_call => { callable => 1 },
734             forward_static_call_array => { callable => 1 },
735             serialize => {},
736             unserialize => {},
737             var_dump => {},
738             var_export => {},
739             debug_zval_dump => {},
740             print_r => {},
741             memory_get_usage => {},
742             memory_get_peak_usage => {},
743             register_shutdown_function => { callable => 1 },
744             register_tick_function => { callable => 1 },
745             unregister_tick_function => {},
746             highlight_file => {},
747             show_source => {},
748             highlight_string => {},
749             php_strip_whitespace => {},
750             ini_get => {},
751             ini_get_all => {},
752             ini_set => {},
753             ini_alter => {},
754             ini_restore => {},
755             get_include_path => {},
756             set_include_path => {},
757             restore_include_path => {},
758             setcookie => {},
759             setrawcookie => {},
760             header => {},
761             header_remove => {},
762             headers_sent => {},
763             headers_list => {},
764             connection_aborted => {},
765             connection_status => {},
766             ignore_user_abort => {},
767             parse_ini_file => {},
768             parse_ini_string => {},
769             is_uploaded_file => {},
770             move_uploaded_file => {},
771             gethostbyaddr => {},
772             gethostbyname => {},
773             gethostbynamel => {},
774             gethostname => {},
775             dns_check_record => {},
776             checkdnsrr => {},
777             dns_get_mx => {},
778             getmxrr => {},
779             dns_get_record => {},
780             intval => { ret => R_INT },
781             floatval => { ret => R_FLOAT },
782             doubleval => { ret => R_FLOAT },
783             strval => { ret => R_STR },
784             gettype => {},
785             settype => {},
786             empty => { ret => R_BOOL },
787             isset => { ret => R_BOOL },
788             is_null => { ret => R_BOOL },
789             is_resource => { ret => R_BOOL },
790             is_bool => { ret => R_BOOL },
791             is_long => { ret => R_BOOL },
792             is_float => { ret => R_BOOL },
793             is_int => { ret => R_BOOL },
794             is_integer => { ret => R_BOOL },
795             is_double => { ret => R_BOOL },
796             is_real => { ret => R_BOOL },
797             is_numeric => { ret => R_BOOL },
798             is_string => { ret => R_BOOL },
799             is_array => { ret => R_BOOL },
800             is_object => { ret => R_BOOL },
801             is_scalar => { ret => R_BOOL },
802             is_callable => { ret => R_BOOL },
803             pclose => {},
804             popen => {},
805             readfile => {},
806             rewind => {},
807             rmdir => {},
808             umask => {},
809             fclose => {},
810             feof => {},
811             fgetc => {},
812             fgets => {},
813             fgetss => {},
814             fread => {},
815             fopen => {},
816             fpassthru => {},
817             ftruncate => {},
818             fstat => {},
819             fseek => {},
820             ftell => {},
821             fflush => {},
822             fwrite => {},
823             fputs => {},
824             mkdir => {},
825             rename => {},
826             copy => {},
827             tempnam => {},
828             tmpfile => {},
829             file => {},
830             file_get_contents => {},
831             file_put_contents => {},
832             stream_select => {},
833             stream_context_create => {},
834             stream_context_set_params => {},
835             stream_context_get_params => {},
836             stream_context_set_option => {},
837             stream_context_get_options => {},
838             stream_context_get_default => {},
839             stream_context_set_default => {},
840             stream_filter_prepend => {},
841             stream_filter_append => {},
842             stream_filter_remove => {},
843             stream_socket_client => {},
844             stream_socket_server => {},
845             stream_socket_accept => {},
846             stream_socket_get_name => {},
847             stream_socket_recvfrom => {},
848             stream_socket_sendto => {},
849             stream_socket_enable_crypto => {},
850             stream_socket_shutdown => {},
851             stream_socket_pair => {},
852             stream_copy_to_stream => {},
853             stream_get_contents => {},
854             stream_supports_lock => {},
855             fgetcsv => {},
856             fputcsv => {},
857             flock => {},
858             get_meta_tags => {},
859             stream_set_read_buffer => {},
860             stream_set_write_buffer => {},
861             set_file_buffer => {},
862             set_socket_blocking => {},
863             stream_set_blocking => {},
864             socket_set_blocking => {},
865             stream_get_meta_data => {},
866             stream_get_line => {},
867             stream_wrapper_register => {},
868             stream_register_wrapper => {},
869             stream_wrapper_unregister => {},
870             stream_wrapper_restore => {},
871             stream_get_wrappers => {},
872             stream_get_transports => {},
873             stream_resolve_include_path => {},
874             stream_is_local => {},
875             get_headers => {},
876             stream_set_timeout => {},
877             socket_set_timeout => {},
878             socket_get_status => {},
879             realpath => {},
880             fnmatch => {},
881             fsockopen => {},
882             pfsockopen => {},
883             pack => {},
884             unpack => {},
885             get_browser => {},
886             crypt => {},
887             opendir => {},
888             closedir => {},
889             chdir => {},
890             chroot => {},
891             getcwd => {},
892             rewinddir => {},
893             readdir => {},
894             dir => {},
895             scandir => {},
896             glob => {},
897             fileatime => {},
898             filectime => {},
899             filegroup => {},
900             fileinode => {},
901             filemtime => {},
902             fileowner => {},
903             fileperms => {},
904             filesize => {},
905             filetype => {},
906             file_exists => {},
907             is_writable => {},
908             is_writeable => {},
909             is_readable => {},
910             is_executable => {},
911             is_file => {},
912             is_dir => {},
913             is_link => {},
914             stat => {},
915             lstat => {},
916             chown => {},
917             chgrp => {},
918             lchown => {},
919             lchgrp => {},
920             chmod => {},
921             touch => {},
922             clearstatcache => {},
923             disk_total_space => {},
924             disk_free_space => {},
925             diskfreespace => {},
926             realpath_cache_size => {},
927             realpath_cache_get => {},
928             mail => {},
929             ezmlm_hash => {},
930             openlog => {},
931             syslog => {},
932             closelog => {},
933             define_syslog_variables => {},
934             lcg_value => {},
935             metaphone => {},
936             ob_start => { cmd => \&PHP::Decode::Transformer::cmd_ob_start, ret => R_BOOL, callable => 1 },
937             ob_flush => {},
938             ob_clean => {},
939             ob_end_flush => { cmd => \&PHP::Decode::Transformer::cmd_ob_end_flush, ret => R_BOOL, callable => 1 },
940             ob_end_clean => { cmd => \&PHP::Decode::Transformer::cmd_ob_end_clean, ret => R_BOOL },
941             ob_get_flush => {},
942             ob_get_clean => {},
943             ob_get_length => {},
944             ob_get_level => {},
945             ob_get_status => {},
946             ob_get_contents => {},
947             ob_implicit_flush => {},
948             ob_list_handlers => {},
949             ksort => { param => ['#ref0'], ret => R_BOOL },
950             krsort => { param => ['#ref0'], ret => R_BOOL },
951             natsort => { param => ['#ref0'], ret => R_BOOL },
952             natcasesort => { param => ['#ref0'], ret => R_BOOL },
953             asort => { param => ['#ref0'], ret => R_BOOL },
954             arsort => { param => ['#ref0'], ret => R_BOOL },
955             sort => { param => ['#ref0'], ret => R_BOOL },
956             rsort => { param => ['#ref0'], ret => R_BOOL },
957             usort => { param => ['#ref0'], ret => R_BOOL, callable => 1 },
958             uasort => { param => ['#ref0'], ret => R_BOOL, callable => 1 },
959             uksort => { param => ['#ref0'], ret => R_BOOL, callable => 1 },
960             shuffle => { param => ['#ref0'], ret => R_BOOL },
961             array_walk => { param => ['#ref0'], ret => R_BOOL, callable => 1 },
962             array_walk_recursive => { param => ['#ref0'], ret => R_BOOL, callable => 1 },
963             count => {},
964             end => { param => ['#ref0'], ret => R_MIXED },
965             prev => { param => ['#ref0'], ret => R_MIXED },
966             next => { param => ['#ref0'], ret => R_MIXED },
967             reset => { param => ['#ref0'], ret => R_MIXED },
968             current => {},
969             key => {},
970             min => {},
971             max => {},
972             in_array => {},
973             array_search => {},
974             extract => { param => ['#ref0'], ret => R_INT },
975             compact => {},
976             array_fill => {},
977             array_fill_keys => {},
978             range => { ret => R_ARRAY },
979             array_multisort => { param => ['#ref0'], ret => R_BOOL },
980             array_push => { param => ['#ref0'], ret => R_INT },
981             array_pop => { param => ['#ref0'], ret => R_MIXED },
982             array_shift => { param => ['#ref0'], ret => R_MIXED },
983             array_unshift => { param => ['#ref0'], ret => R_INT },
984             array_splice => { param => ['#ref0'], ret => R_ARRAY },
985             array_slice => {},
986             array_merge => {},
987             array_merge_recursive => {},
988             array_replace => {},
989             array_replace_recursive => {},
990             array_keys => {},
991             array_values => {},
992             array_count_values => {},
993             array_reverse => {},
994             array_reduce => {},
995             array_pad => {},
996             array_flip => {},
997             array_change_key_case => {},
998             array_rand => {},
999             array_unique => {},
1000             array_intersect => {},
1001             array_intersect_key => {},
1002             array_intersect_ukey => { callable => 1 },
1003             array_uintersect => { callable => 1 },
1004             array_intersect_assoc => {},
1005             array_uintersect_assoc => { callable => 1 },
1006             array_intersect_uassoc => { callable => 1 },
1007             array_uintersect_uassoc => { callable => 1 },
1008             array_diff => {},
1009             array_diff_key => {},
1010             array_diff_ukey => { callable => 1 },
1011             array_udiff => { callable => 1 },
1012             array_diff_assoc => {},
1013             array_udiff_assoc => { callable => 1 },
1014             array_diff_uassoc => { callable => 1 },
1015             array_udiff_uassoc => { callable => 1 },
1016             array_sum => {},
1017             array_product => {},
1018             array_filter => { callable => 1 },
1019             array_map => { callable => 1, ret => R_ARRAY },
1020             array_chunk => {},
1021             array_combine => {},
1022             array_key_exists => {},
1023             pos => {},
1024             sizeof => {},
1025             key_exists => {},
1026             assert => {},
1027             assert_options => {},
1028             version_compare => {},
1029             ftok => {},
1030             str_rot13 => {},
1031             stream_get_filters => {},
1032             stream_filter_register => {},
1033             stream_bucket_make_writeable => {},
1034             stream_bucket_prepend => {},
1035             stream_bucket_append => {},
1036             stream_bucket_new => {},
1037             output_add_rewrite_var => {},
1038             output_reset_rewrite_vars => {},
1039             sys_get_temp_dir => {},
1040             );
1041              
1042             my %php_funcs_date = (
1043             strtotime => {},
1044             date => {},
1045             idate => {},
1046             gmdate => {},
1047             mktime => {},
1048             gmmktime => {},
1049             checkdate => {},
1050             strftime => {},
1051             gmstrftime => {},
1052             time => {},
1053             localtime => {},
1054             getdate => {},
1055             date_create => {},
1056             date_create_from_format => {},
1057             date_parse => {},
1058             date_parse_from_format => {},
1059             date_get_last_errors => {},
1060             date_format => {},
1061             date_modify => {},
1062             date_add => {},
1063             date_sub => {},
1064             date_timezone_get => {},
1065             date_timezone_set => {},
1066             date_offset_get => {},
1067             date_diff => {},
1068             date_time_set => {},
1069             date_date_set => {},
1070             date_isodate_set => {},
1071             date_timestamp_set => {},
1072             date_timestamp_get => {},
1073             timezone_open => {},
1074             timezone_name_get => {},
1075             timezone_name_from_abbr => {},
1076             timezone_offset_get => {},
1077             timezone_transitions_get => {},
1078             timezone_location_get => {},
1079             timezone_identifiers_list => {},
1080             timezone_abbreviations_list => {},
1081             timezone_version_get => {},
1082             date_interval_create_from_date_string => {},
1083             date_interval_format => {},
1084             date_default_timezone_set => {},
1085             date_default_timezone_get => {},
1086             date_sunrise => {},
1087             date_sunset => {},
1088             date_sun_info => {},
1089             );
1090              
1091             my %php_funcs_pcre = (
1092             preg_match => {},
1093             preg_match_all => {},
1094             preg_replace => { ret => R_STR|R_ARRAY },
1095             preg_replace_callback => { callable => 1, ret => R_STR|R_ARRAY },
1096             preg_replace_callback_array => { callable => 1, ret => R_STR|R_ARRAY },
1097             preg_filter => {},
1098             preg_split => { ret => R_ARRAY },
1099             preg_quote => {},
1100             preg_grep => {},
1101             preg_last_error => {},
1102             );
1103              
1104             my %php_funcs_posix = (
1105             posix_kill => {},
1106             posix_getpid => {},
1107             posix_getppid => {},
1108             posix_getuid => {},
1109             posix_setuid => {},
1110             posix_geteuid => {},
1111             posix_seteuid => {},
1112             posix_getgid => {},
1113             posix_setgid => {},
1114             posix_getegid => {},
1115             posix_setegid => {},
1116             posix_getgroups => {},
1117             posix_getlogin => {},
1118             posix_getpgrp => {},
1119             posix_setsid => {},
1120             posix_setpgid => {},
1121             posix_getpgid => {},
1122             posix_getsid => {},
1123             posix_uname => {},
1124             posix_times => {},
1125             posix_ctermid => {},
1126             posix_ttyname => {},
1127             posix_isatty => {},
1128             posix_getcwd => {},
1129             posix_mkfifo => {},
1130             posix_mknod => {},
1131             posix_access => {},
1132             posix_getgrnam => {},
1133             posix_getgrgid => {},
1134             posix_getpwnam => {},
1135             posix_getpwuid => {},
1136             posix_getrlimit => {},
1137             posix_get_last_error => {},
1138             posix_errno => {},
1139             posix_strerror => {},
1140             posix_initgroups => {},
1141             );
1142              
1143             my %php_funcs_curl = (
1144             curl_init => {},
1145             curl_copy_handle => {},
1146             curl_version => {},
1147             curl_setopt => {},
1148             curl_setopt_array => {},
1149             curl_exec => {},
1150             curl_getinfo => {},
1151             curl_error => {},
1152             curl_errno => {},
1153             curl_close => {},
1154             curl_multi_init => {},
1155             curl_multi_add_handle => {},
1156             curl_multi_remove_handle => {},
1157             curl_multi_select => {},
1158             curl_multi_exec => {},
1159             curl_multi_getcontent => {},
1160             curl_multi_info_read => {},
1161             curl_multi_close => {},
1162             );
1163              
1164             my %php_funcs_zlib = (
1165             readgzfile => {},
1166             gzrewind => {},
1167             gzclose => {},
1168             gzeof => {},
1169             gzgetc => {},
1170             gzgets => {},
1171             gzgetss => {},
1172             gzread => {},
1173             gzopen => {},
1174             gzpassthru => {},
1175             gzseek => {},
1176             gztell => {},
1177             gzwrite => {},
1178             gzputs => {},
1179             gzfile => {},
1180             gzcompress => {},
1181             gzuncompress => {},
1182             gzdeflate => {},
1183             gzinflate => {},
1184             gzdecode => {},
1185             gzencode => {},
1186             ob_gzhandler => {},
1187             zlib_get_coding_type => {},
1188             );
1189              
1190             my %php_funcs_mysql = (
1191             mysql_connect => {},
1192             mysql_pconnect => {},
1193             mysql_close => {},
1194             mysql_select_db => {},
1195             mysql_query => {},
1196             mysql_unbuffered_query => {},
1197             mysql_db_query => {},
1198             mysql_list_dbs => {},
1199             mysql_list_tables => {},
1200             mysql_list_fields => {},
1201             mysql_list_processes => {},
1202             mysql_error => {},
1203             mysql_errno => {},
1204             mysql_affected_rows => {},
1205             mysql_insert_id => {},
1206             mysql_result => {},
1207             mysql_num_rows => {},
1208             mysql_num_fields => {},
1209             mysql_fetch_row => {},
1210             mysql_fetch_array => {},
1211             mysql_fetch_assoc => {},
1212             mysql_fetch_object => {},
1213             mysql_data_seek => {},
1214             mysql_fetch_lengths => {},
1215             mysql_fetch_field => {},
1216             mysql_field_seek => {},
1217             mysql_free_result => {},
1218             mysql_field_name => {},
1219             mysql_field_table => {},
1220             mysql_field_len => {},
1221             mysql_field_type => {},
1222             mysql_field_flags => {},
1223             mysql_escape_string => {},
1224             mysql_real_escape_string => {},
1225             mysql_stat => {},
1226             mysql_thread_id => {},
1227             mysql_client_encoding => {},
1228             mysql_ping => {},
1229             mysql_get_client_info => {},
1230             mysql_get_host_info => {},
1231             mysql_get_proto_info => {},
1232             mysql_get_server_info => {},
1233             mysql_info => {},
1234             mysql_set_charset => {},
1235             mysql => {},
1236             mysql_fieldname => {},
1237             mysql_fieldtable => {},
1238             mysql_fieldlen => {},
1239             mysql_fieldtype => {},
1240             mysql_fieldflags => {},
1241             mysql_selectdb => {},
1242             mysql_freeresult => {},
1243             mysql_numfields => {},
1244             mysql_numrows => {},
1245             mysql_listdbs => {},
1246             mysql_listtables => {},
1247             mysql_listfields => {},
1248             mysql_db_name => {},
1249             mysql_dbname => {},
1250             mysql_tablename => {},
1251             mysql_table_name => {},
1252             );
1253              
1254             my %php_funcs_hash = (
1255             hash => { ret => R_STR },
1256             hash_file => {},
1257             hash_hmac => {},
1258             hash_hmac_file => {},
1259             hash_init => {},
1260             hash_update => {},
1261             hash_update_stream => {},
1262             hash_update_file => {},
1263             hash_final => {},
1264             hash_copy => {},
1265             hash_algos => {},
1266             hash_hmac_algos => {},
1267             hash_pbkdf2 => {},
1268             hash_equals => {},
1269             hash_hkdf => {},
1270             mhash_get_block_size => {},
1271             mhash_get_hash_name => {},
1272             mhash_keygen_s2k => {},
1273             mhash_count => {},
1274             mhash => {},
1275             );
1276              
1277             my %php_funcs_libxml = (
1278             libxml_set_streams_context => {},
1279             libxml_use_internal_errors => {},
1280             libxml_get_last_error => {},
1281             libxml_get_errors => {},
1282             libxml_clear_errors => {},
1283             libxml_disable_entity_loader => {},
1284             libxml_set_external_entity_loader => {},
1285             );
1286              
1287             my %php_funcs_filter = (
1288             filter_has_var => { ret => R_BOOL },
1289             filter_input => { ret => R_MIXED },
1290             filter_var => { ret => R_MIXED },
1291             filter_input_array => { ret => R_MIXED },
1292             filter_var_array => { ret => R_ARRAY|R_BOOL },
1293             filter_list => { ret => R_ARRAY },
1294             filter_id => { ret => R_INT|R_BOOL },
1295             );
1296              
1297             my %php_funcs_json = (
1298             json_encode => { ret => R_STR },
1299             json_decode => { ret => R_MIXED },
1300             json_last_error => { ret => R_INT },
1301             json_last_error_msg => { ret => R_STR },
1302             );
1303              
1304             my %php_funcs_spl = (
1305             class_implements => {},
1306             class_parents => {},
1307             class_uses => {},
1308             spl_autoload => {},
1309             spl_autoload_call => {},
1310             spl_autoload_extensions => {},
1311             spl_autoload_functions => {},
1312             spl_autoload_register => {},
1313             spl_autoload_unregister => {},
1314             spl_classes => {},
1315             spl_object_hash => {},
1316             spl_object_id => {},
1317             iterator_apply => {},
1318             iterator_count => {},
1319             iterator_to_array => {},
1320             );
1321              
1322             my %php_funcs_session = (
1323             session_name => { ret => R_STR },
1324             session_module_name => { ret => R_STR },
1325             session_save_path => { ret => R_STR },
1326             session_id => { ret => R_STR },
1327             session_create_id => { ret => R_STR },
1328             session_regenerate_id => { ret => R_BOOL },
1329             session_decode => { ret => R_BOOL },
1330             session_encode => { ret => R_STR },
1331             session_destroy => { ret => R_BOOL },
1332             session_unset => { ret => R_BOOL },
1333             session_gc => { ret => R_INT },
1334             session_get_cookie_params => { ret => R_BOOL },
1335             session_write_close => { ret => R_BOOL },
1336             session_abort => { ret => R_BOOL },
1337             session_reset => { ret => R_BOOL },
1338             session_status => { ret => R_INT },
1339             session_register_shutdown => { ret => R_VOID },
1340             session_commit => { ret => R_BOOL },
1341             session_set_save_handler => { ret => R_BOOL },
1342             session_cache_limiter => { ret => R_STR },
1343             session_cache_expire => { ret => R_INT },
1344             session_set_cookie_params => { ret => R_ARRAY },
1345             session_start => { ret => R_BOOL },
1346             );
1347              
1348             my %php_funcs_pdo = (
1349             pdo_drivers => {},
1350             );
1351              
1352             my %php_funcs_xml = (
1353             xml_parser_create => {},
1354             xml_parser_create_ns => {},
1355             xml_set_object => {},
1356             xml_set_element_handler => {},
1357             xml_set_character_data_handler => {},
1358             xml_set_processing_instruction_handler => {},
1359             xml_set_default_handler => {},
1360             xml_set_unparsed_entity_decl_handler => {},
1361             xml_set_notation_decl_handler => {},
1362             xml_set_external_entity_ref_handler => {},
1363             xml_set_start_namespace_decl_handler => {},
1364             xml_set_end_namespace_decl_handler => {},
1365             xml_parse => {},
1366             xml_parse_into_struct => {},
1367             xml_get_error_code => {},
1368             xml_error_string => {},
1369             xml_get_current_line_number => {},
1370             xml_get_current_column_number => {},
1371             xml_get_current_byte_index => {},
1372             xml_parser_free => {},
1373             xml_parser_set_option => {},
1374             xml_parser_get_option => {},
1375             );
1376              
1377             my %php_funcs_calendar = (
1378             cal_days_in_month => {},
1379             cal_from_jd => {},
1380             cal_info => {},
1381             cal_to_jd => {},
1382             easter_date => {},
1383             easter_days => {},
1384             frenchtojd => {},
1385             gregoriantojd => {},
1386             jddayofweek => {},
1387             jdmonthname => {},
1388             jdtofrench => {},
1389             jdtogregorian => {},
1390             jdtojewish => {},
1391             jdtojulian => {},
1392             jdtounix => {},
1393             jewishtojd => {},
1394             juliantojd => {},
1395             unixtojd => {},
1396             );
1397              
1398             my %php_funcs_ctype = (
1399             ctype_alnum => { ret => R_BOOL },
1400             ctype_alpha => { ret => R_BOOL },
1401             ctype_cntrl => { ret => R_BOOL },
1402             ctype_digit => { ret => R_BOOL },
1403             ctype_lower => { ret => R_BOOL },
1404             ctype_graph => { ret => R_BOOL },
1405             ctype_print => { ret => R_BOOL },
1406             ctype_punct => { ret => R_BOOL },
1407             ctype_space => { ret => R_BOOL },
1408             ctype_upper => { ret => R_BOOL },
1409             ctype_xdigit => { ret => R_BOOL },
1410             );
1411              
1412             my %php_funcs_gettext = (
1413             textdomain => { ret => R_STR },
1414             gettext => { ret => R_STR },
1415             '_' => { ret => R_STR },
1416             dgettext => { ret => R_STR },
1417             dcgettext => { ret => R_STR },
1418             bindtextdomain => { ret => R_STR },
1419             ngettext => { ret => R_STR},
1420             dngettext => { ret => R_STR },
1421             dcngettext => { ret => R_STR },
1422             bind_textdomain_codeset => { ret => R_STR },
1423             );
1424              
1425             my %php_funcs_iconv = (
1426             iconv_strlen => { ret => R_INT },
1427             iconv_substr => { ret => R_STR },
1428             iconv_strpos => { ret => R_INT },
1429             iconv_strrpos => { ret => R_INT },
1430             iconv_mime_encode => { ret => R_STR },
1431             iconv_mime_decode => { ret => R_STR },
1432             iconv_mime_decode_headers => { ret => R_STR|R_ARRAY },
1433             iconv => { ret => R_STR },
1434             iconv_set_encoding => { ret => R_BOOL },
1435             iconv_get_encoding => { ret => R_STR|R_ARRAY },
1436             ob_iconv_handler => { callable => 1, ret => R_STR },
1437             );
1438              
1439             my %php_funcs_socket = (
1440             socket_select => {},
1441             socket_create_listen => {},
1442             socket_accept => {},
1443             socket_set_nonblock => {},
1444             socket_set_block => {},
1445             socket_listen => {},
1446             socket_close => {},
1447             socket_write => {},
1448             socket_read => { ret => R_STR },
1449             socket_getsockname => {},
1450             socket_getpeername => {},
1451             socket_create => {},
1452             socket_connect => {},
1453             socket_strerror => {},
1454             socket_bind => {},
1455             socket_recv => {},
1456             socket_send => {},
1457             socket_recvfrom => {},
1458             socket_sendto => {},
1459             socket_get_option => {},
1460             socket_getopt => {},
1461             socket_set_option => {},
1462             socket_setopt => {},
1463             socket_create_pair => {},
1464             socket_shutdown => {},
1465             socket_last_error => {},
1466             socket_clear_error => {},
1467             socket_import_stream => {},
1468             socket_export_stream => {},
1469             socket_sendmsg => {},
1470             socket_recvmsg => {},
1471             socket_cmsg_space => {},
1472             socket_addrinfo_lookup => {},
1473             socket_addrinfo_connect => {},
1474             socket_addrinfo_bind => {},
1475             socket_addrinfo_explain => {},
1476             );
1477              
1478             my %php_funcs_tokenizer = (
1479             token_get_all => {},
1480             token_name => {},
1481             );
1482              
1483             # https://developer.wordpress.org/reference/functions/
1484             #
1485             my %php_funcs_wordpress = (
1486             get_option => { ret => R_MIXED },
1487             update_option => { ret => R_BOOL },
1488             add_post_meta => { ret => R_INT },
1489             get_categories => { ret => R_ARRAY },
1490             wp_add_post_tags => { ret => R_ARRAY },
1491             wp_create_category => { ret => R_INT },
1492             get_posts => { ret => R_MIXED },
1493             get_post => { ret => R_MIXED },
1494             wp_insert_post => { ret => R_INT },
1495             wp_update_post => { ret => R_INT },
1496             wp_delete_post => { ret => R_MIXED },
1497             wp_set_post_tags => { ret => R_ARRAY },
1498             get_post_meta => { ret => R_MIXED },
1499             add_post_meta => { ret => R_INT },
1500             update_post_meta => { ret => R_INT },
1501             delete_post_meta => { ret => R_BOOL },
1502             get_users => { ret => R_ARRAY },
1503             get_user_by => { ret => R_MIXED },
1504             get_userdata => { ret => R_MIXED },
1505             wp_insert_user => { ret => R_INT },
1506             wp_update_user => { ret => R_INT },
1507             wp_delete_user => { ret => R_BOOL },
1508             get_user_meta => { ret => R_MIXED },
1509             add_user_meta => { ret => R_INT },
1510             update_user_meta => { ret => R_INT },
1511             delete_user_meta => { ret => R_BOOL },
1512             get_user_setting => { ret => R_MIXED },
1513             delete_user_setting => { ret => R_BOOL },
1514             wp_schedule_single_event => { ret => R_BOOL },
1515             wp_clear_scheduled_hook => { ret => R_INT },
1516             wp_unschedule_event => { ret => R_BOOL },
1517             wp_upload_dir => { ret => R_ARRAY },
1518             );
1519              
1520             my %php_funcs = (
1521             Core => \%php_funcs_core,
1522             standard => \%php_funcs_standard,
1523             date => \%php_funcs_date,
1524             pcre => \%php_funcs_pcre,
1525             posix => \%php_funcs_posix,
1526             curl => \%php_funcs_curl,
1527             zlib => \%php_funcs_zlib,
1528             mysql => \%php_funcs_mysql,
1529             hash => \%php_funcs_hash,
1530             libxml => \%php_funcs_libxml,
1531             filter => \%php_funcs_filter,
1532             json => \%php_funcs_json,
1533             spl => \%php_funcs_spl,
1534             session => \%php_funcs_session,
1535             pdo => \%php_funcs_pdo,
1536             xml => \%php_funcs_xml,
1537             calendar => \%php_funcs_calendar,
1538             ctype => \%php_funcs_ctype,
1539             gettext => \%php_funcs_gettext,
1540             iconv => \%php_funcs_iconv,
1541             socket => \%php_funcs_socket,
1542             tokenizer=> \%php_funcs_tokenizer,
1543             #wordpress=> \%php_funcs_wordpress,
1544             );
1545              
1546             sub get_php_func {
1547 1450     1450 0 2617 my ($cmd) = @_;
1548              
1549 1450         2205 $cmd = lc($cmd);
1550              
1551 1450         6976 foreach my $e (keys %php_funcs) {
1552 24715 100       46656 if (exists $php_funcs{$e}{$cmd}) {
1553 634         1093 my $f = $php_funcs{$e}{$cmd};
1554 634         1853 return $f;
1555             }
1556             }
1557             return
1558 816         2350 }
1559              
1560             sub func_may_call_callbacks {
1561 321     321 0 775 my ($cmd) = @_;
1562              
1563 321         645 $cmd = lc($cmd);
1564              
1565 321 50       1232 if ($cmd =~ /^(eval|include|include_once|require|require_once)$/) {
1566 0         0 return 1;
1567             }
1568 321         676 my $f = get_php_func($cmd);
1569 321 100       1037 if (defined $f) {
1570 95 100       236 if (exists $f->{callable}) {
1571 5         18 return 1;
1572             }
1573 90         352 return 0;
1574             }
1575 226         688 return 1;
1576             }
1577              
1578             sub func_may_return_string {
1579 3     3 0 9 my ($cmd) = @_;
1580              
1581 3         7 $cmd = lc($cmd);
1582              
1583 3 50       14 if ($cmd =~ /^(include|include_once|require|require_once)$/) {
1584 0         0 return 0;
1585             }
1586 3         8 my $f = get_php_func($cmd);
1587 3 50       11 if (defined $f) {
1588 3 50       9 if (exists $f->{ret}) {
1589 3 100       13 unless ($f->{ret} & (R_STR|R_MIXED)) {
1590 1         4 return 0;
1591             }
1592 2 50       8 if ($f->{ret} & R_FIX) {
1593 0         0 return 0;
1594             }
1595             }
1596 2         7 return 1;
1597             }
1598 0         0 return 1;
1599             }
1600              
1601             sub cmd_base64_decode {
1602 22     22 0 66 my ($ctx, $cmd, $args) = @_;
1603 22         43 my $parser = $ctx->{parser};
1604              
1605 22 50       58 if (scalar @$args >= 1) {
1606 22         77 my $s = $parser->get_strval($$args[0]);
1607 22         37 my $php50_compat;
1608 22         44 my $strict = 0;
1609 22 50       62 if (scalar @$args > 1) {
1610 0         0 $strict = $parser->get_strval($$args[1]);
1611             }
1612 22 100       54 if (defined $s) {
1613             # older php-versions (< php5.1) treated blanks in base64 string as '+':
1614             # https://php.net/manual/en/function.base64-decode.php
1615             #
1616             # perl ignores any character not part of the 65-character base64 subset.
1617             # https://perldoc.perl.org/MIME::Base64
1618             #
1619 18 50       35 if ($php50_compat) {
1620 0         0 $s =~ s/ /+/g; # incompatible with ignore-rule
1621             }
1622 18 50 33     54 if ($strict && !($s =~ /^[A-Za-z0-9\/+]*=*$/)) {
1623 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$$args[0] contains non-strict chars");
1624             }
1625 18         31 my $decoded;
1626             {
1627             # suppress decode_base64() warnings (premature end of data, etc.)
1628 18         30 local $^W = 0;
  18         83  
1629 18         110 $decoded = decode_base64($s);
1630             }
1631 18 50       47 if (defined $decoded) {
1632             #$decoded = decode_input($decoded); # might convert wide strings back to internal perl utf-8 encoding
1633             #$ctx->{log}->($ctx, 'cmd', $cmd, "%d:[%s]->%d:[%s]", length($s), $parser->shortstr($s,40), length($decoded), $parser->shortstr($decoded,40)) if $ctx->{log};
1634 18         59 return $parser->setstr($decoded);
1635             }
1636             }
1637             }
1638 4         9 return;
1639             }
1640              
1641             sub cmd_base64_encode {
1642 3     3 0 8 my ($ctx, $cmd, $args) = @_;
1643 3         7 my $parser = $ctx->{parser};
1644              
1645 3 50       10 if (scalar @$args == 1) {
1646 3         11 my $s = $parser->get_strval($$args[0]);
1647 3 50       9 if (defined $s) {
1648             # might convert from internal perl wide string representation to utf-8 stream
1649             # see: https://perldoc.perl.org/MIME::Base64
1650             #
1651             #my $bytestr = encode('utf-8', $s);
1652             #my $encoded = encode_base64($bytestr,'');
1653 3         14 my $encoded = encode_base64($s,'');
1654 3 50       9 if (defined $encoded) {
1655 3 50       9 $ctx->{log}->($ctx, 'cmd', $cmd, "%d:[%s]->%d:[%s]", length($s), $parser->shortstr($s,40), length($encoded), $parser->shortstr($encoded,40)) if $ctx->{log};
1656 3         13 return $parser->setstr($encoded);
1657             }
1658             }
1659             }
1660 0         0 return;
1661             }
1662              
1663             sub get_refvar_val {
1664 22     22 0 48 my ($ctx, $var) = @_;
1665 22         35 my $parser = $ctx->{parser};
1666              
1667 22 50 33     60 unless ($ctx->{infunction} && !$ctx->{incall}) {
1668 22 100       54 if (is_variable($var)) {
    50          
1669 21         62 my $val = $ctx->getvar($var, 1);
1670 21         59 return (1, $val); # valid
1671             } elsif ($var =~ /^(\#elem\d+)$/) {
1672 1         3 my ($v, $i) = @{$parser->{strmap}{$var}};
  1         7  
1673 1         6 my ($basevar, $has_index, $idxstr) = $ctx->resolve_variable($var, 0);
1674 1 50       4 if ($has_index) {
1675 1         8 my $basestr = $ctx->exec_statement($basevar, 0);
1676 1 50 33     12 if (defined $basestr && is_array($basestr) && defined $idxstr) {
      33        
1677 1 50       11 $idxstr = $parser->setstr('') if is_null($idxstr); # null maps to '' array index
1678 1         6 my $arr = $parser->{strmap}{$basestr};
1679 1         8 my $val = $arr->get($idxstr);
1680 1         12 return (1, $val); # valid
1681             }
1682             }
1683             }
1684             }
1685 0         0 return;
1686             }
1687              
1688             sub set_refvar_val {
1689 8     8 0 21 my ($ctx, $var, $val) = @_;
1690 8         14 my $parser = $ctx->{parser};
1691              
1692 8 50       24 if (is_variable($var)) {
    0          
1693 8         25 $ctx->setvar($var, $val, 1);
1694             } elsif ($var =~ /^(\#elem\d+)$/) {
1695 0         0 my $sub = $parser->setexpr('=', $var, $val);
1696 0         0 my $had_assigns = $ctx->have_assignments();
1697 0         0 my $k = $ctx->exec_statement($sub);
1698 0 0       0 if (!$had_assigns) {
1699             # remove any pending assignments, to avoid variable
1700             # insertion for the 'sub'-assignmenmt.
1701             # The full array assignment is inserted after the loop
1702             #
1703 0         0 $ctx->discard_pending_assignments();
1704             }
1705             }
1706 8         21 return;
1707             }
1708              
1709             sub exec_cmd {
1710 481     481 1 1100 my ($ctx, $cmd, $args) = @_;
1711 481         872 my $parser = $ctx->{parser};
1712 481         658 my $res;
1713 481         652 my $to_num = 0;
1714              
1715             # some dynamic function names are created from mixed-style strings
1716             # see: https://www.php.net/manual/en/functions.user-defined.php
1717             # function names are case-insensitive for the ASCII characters A to Z.
1718             #
1719 481         827 $cmd = lc($cmd);
1720              
1721 481 50       1054 $ctx->{log}->($ctx, 'cmd', $cmd, "args (%s)", join(', ', @$args)) if $ctx->{log};
1722              
1723 481         896 my $f = get_php_func($cmd);
1724              
1725 481 100 100     1638 if (defined $f && exists $f->{cmd}) {
1726 27         58 my $k = &{$f->{cmd}}(@_);
  27         80  
1727 27         86 return $k;
1728             }
1729              
1730 454 50 33     23445 if ($cmd eq 'unescape') {
    50 100        
    100 66        
    100 33        
    50 33        
    50 66        
    50 33        
    50          
    50          
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    100          
    50          
    50          
    100          
    50          
    100          
    100          
    100          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    100          
    50          
    50          
1731 0 0       0 if (scalar @$args == 1) {
1732 0         0 my $s = $parser->get_strval($$args[0]);
1733 0 0       0 if (defined $s) {
1734 0         0 $res = $s;
1735 0         0 $res =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
  0         0  
1736             }
1737             }
1738             } elsif ($cmd eq 'str_rot13') {
1739 0 0       0 if (scalar @$args == 1) {
1740 0         0 my $s = $parser->get_strval($$args[0]);
1741 0 0       0 if (defined $s) {
1742 0         0 $res = $s;
1743 0         0 $res =~ tr/A-Za-z/N-ZA-Mn-za-m/;
1744             }
1745             }
1746             } elsif ($cmd eq 'strrev') {
1747 1 50       5 if (scalar @$args == 1) {
1748 1         4 my $s = $parser->get_strval($$args[0]);
1749 1 50       4 if (defined $s) {
1750 1         3 $res = reverse $s;
1751             }
1752             }
1753             } elsif ($cmd eq 'strtoupper') {
1754 3 50       11 if (scalar @$args == 1) {
1755 3         11 my $s = $parser->get_strval($$args[0]);
1756 3 100       11 if (defined $s) {
1757 2         8 $res = uc($s);
1758             }
1759             }
1760             } elsif ($cmd eq 'strtolower') {
1761 0 0       0 if (scalar @$args == 1) {
1762 0         0 my $s = $parser->get_strval($$args[0]);
1763 0 0       0 if (defined $s) {
1764 0         0 $res = lc($s);
1765             }
1766             }
1767             } elsif ($cmd eq 'ucfirst') {
1768 0 0       0 if (scalar @$args == 1) {
1769 0         0 my $s = $parser->get_strval($$args[0]);
1770 0 0       0 if (defined $s) {
1771 0         0 $res = ucfirst($s);
1772             }
1773             }
1774             } elsif ($cmd eq 'lcfirst') {
1775 0 0       0 if (scalar @$args == 1) {
1776 0         0 my $s = $parser->get_strval($$args[0]);
1777 0 0       0 if (defined $s) {
1778 0         0 $res = lcfirst($s);
1779             }
1780             }
1781             } elsif ($cmd eq 'dirname') {
1782 0 0       0 if (scalar @$args == 1) {
1783 0         0 my $s = $parser->get_strval($$args[0]);
1784 0 0       0 if (defined $s) {
1785 0         0 $res = dirname($s);
1786             }
1787             }
1788             } elsif ($cmd eq 'basename') {
1789 0 0       0 if (scalar @$args == 1) {
1790 0         0 my $s = $parser->get_strval($$args[0]);
1791 0 0       0 if (defined $s) {
1792 0         0 $res = basename($s);
1793             }
1794             }
1795             } elsif ($cmd eq 'strlen') {
1796 15 50       75 if (scalar @$args == 1) {
1797 15         30 my $val = $$args[0];
1798 15 50       44 if (is_array($val)) {
    100          
    100          
1799 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "len of array $val taken -> 0");
1800 0         0 $res = 0;
1801 0         0 $to_num = 1;
1802             } elsif (is_null($val)) {
1803 1         3 $res = 0;
1804 1         3 $to_num = 1;
1805             } elsif (is_strval($val)) {
1806 8         26 my $s = $parser->get_strval($val);
1807 8 50       27 if (defined $s) {
1808 8         14 $res = length($s);
1809 8         18 $to_num = 1;
1810             }
1811             }
1812             }
1813             } elsif ($cmd eq 'chr') {
1814 8 50       33 if (scalar @$args == 1) {
1815 8         30 my $s = $parser->get_strval($$args[0]);
1816 8 100       28 if (defined $s) {
1817 7         26 $res = chr(int($s) & 0xff);
1818             }
1819             }
1820             } elsif ($cmd eq 'ord') {
1821 13 50       37 if (scalar @$args == 1) {
1822 13         44 my $s = $parser->get_strval($$args[0]);
1823 13 100       34 if (defined $s) {
1824 11         23 $res = ord($s);
1825 11         19 $to_num = 1;
1826             }
1827             }
1828             } elsif ($cmd eq 'hexdec') {
1829 2 50       7 if (scalar @$args == 1) {
1830 2         7 my $s = $parser->get_strval($$args[0]);
1831 2 50       6 if (defined $s) {
1832 2         49 $res = hex($s);
1833 2         41 $to_num = 1;
1834             }
1835             }
1836             } elsif ($cmd eq 'octdec') {
1837 0 0       0 if (scalar @$args == 1) {
1838 0         0 my $s = $parser->get_strval($$args[0]);
1839 0 0       0 if (defined $s) {
1840 0         0 $res = oct($s);
1841 0         0 $to_num = 1;
1842             }
1843             }
1844             } elsif ($cmd eq 'bindec') {
1845 0 0       0 if (scalar @$args == 1) {
1846 0         0 my $s = $parser->get_strval($$args[0]);
1847 0 0       0 if (defined $s) {
1848 0         0 $res = unpack('N', pack('B32', substr('0' x 32 . $s, -32)));
1849 0         0 $to_num = 1;
1850             }
1851             }
1852             } elsif ($cmd eq 'dechex') {
1853 0 0       0 if (scalar @$args == 1) {
1854 0         0 my $s = $parser->get_strval($$args[0]);
1855 0 0       0 if (defined $s) {
1856 0         0 $res = sprintf("%x", $s);
1857             }
1858             }
1859             } elsif ($cmd eq 'octhex') {
1860 0 0       0 if (scalar @$args == 1) {
1861 0         0 my $s = $parser->get_strval($$args[0]);
1862 0 0       0 if (defined $s) {
1863 0         0 $res = sprintf("%o", $s);
1864             }
1865             }
1866             } elsif ($cmd eq 'binhex') {
1867 0 0       0 if (scalar @$args == 1) {
1868 0         0 my $s = $parser->get_strval($$args[0]);
1869 0 0       0 if (defined $s) {
1870 0         0 $res = sprintf("%b", $s);
1871             }
1872             }
1873             } elsif ($cmd eq 'bin2hex') {
1874 3 50       13 if (scalar @$args == 1) {
1875 3         13 my $s = $parser->get_strval($$args[0]);
1876 3 50       12 if (defined $s) {
1877 3         23 $res = unpack('H*', $s);
1878             }
1879             }
1880             } elsif ($cmd eq 'hex2bin') {
1881 1 50       5 if (scalar @$args == 1) {
1882 1         6 my $s = $parser->get_strval($$args[0]);
1883 1 50       4 if (defined $s) {
1884 1         7 $res = pack('H*', $s);
1885             }
1886             }
1887             } elsif ($cmd eq 'floor') {
1888 0 0       0 if (scalar @$args == 1) {
1889 0         0 my $s = $parser->get_strval($$args[0]);
1890 0 0       0 if (defined $s) {
1891 0 0       0 $s = 0 if ($s eq '');
1892 0         0 $res = int($s);
1893 0         0 $to_num = 1;
1894             }
1895             }
1896             } elsif ($cmd eq 'ceil') {
1897 0 0       0 if (scalar @$args == 1) {
1898 0         0 my $s = $parser->get_strval($$args[0]);
1899 0 0       0 if (defined $s) {
1900 0 0       0 $s = 0 if ($s eq '');
1901 0         0 $res = int($s + 0.99);
1902 0         0 $to_num = 1;
1903             }
1904             }
1905             } elsif ($cmd eq 'intval') {
1906 3 50       13 if (scalar @$args == 1) {
1907 3         14 my $s = $parser->get_strval($$args[0]);
1908 3 50       11 if (defined $s) {
1909 3 50       10 $s = 0 if ($s eq '');
1910 3         19 $res = int(PHP::Decode::Op::to_num($s));
1911 3         8 $to_num = 1;
1912             }
1913             }
1914             } elsif ($cmd eq 'strval') {
1915 1 50       6 if (scalar @$args == 1) {
1916 1         5 my $s = $parser->get_strval($$args[0]);
1917 1 50       7 if (defined $s) {
1918 1         2 $res = "$s";
1919             }
1920             }
1921             } elsif ($cmd eq 'boolval') {
1922 0 0       0 if (scalar @$args == 1) {
1923 0         0 my $s = $parser->get_strval($$args[0]);
1924 0 0       0 if (defined $s) {
1925 0 0       0 $s = 0 if ($s eq '');
1926 0 0       0 if ($s eq '0') {
1927 0         0 $res = 0;
1928             } else {
1929 0         0 $res = 1;
1930             }
1931 0         0 $to_num = 1;
1932             }
1933             }
1934             } elsif ($cmd eq 'floatval') {
1935 0 0       0 if (scalar @$args == 1) {
1936 0         0 my $s = $parser->get_strval($$args[0]);
1937 0 0       0 if (defined $s) {
1938 0 0       0 $s = 0 if ($s eq '');
1939 0         0 $res = $s + 0;
1940 0         0 $to_num = 1;
1941             }
1942             }
1943             } elsif ($cmd eq 'pow') {
1944 0 0       0 if (scalar @$args == 2) {
1945 0         0 my $s = $parser->get_strval($$args[0]);
1946 0         0 my $e = $parser->get_strval($$args[1]);
1947 0 0 0     0 if (defined $s && defined $e) {
1948 0 0       0 $s = 0 if ($s eq '');
1949 0 0       0 $e = 0 if ($e eq '');
1950 0         0 $res = $s ** $e;
1951 0         0 $to_num = 1;
1952             }
1953             }
1954             } elsif ($cmd eq 'gzinflate') {
1955 1 50       5 if (scalar @$args >= 1) {
1956 1         5 my $s = $parser->get_strval($$args[0]);
1957 1         4 my $maxlen = '0';
1958 1 50       4 if (scalar @$args > 1) {
1959 0         0 $maxlen = $parser->get_strval($$args[1]);
1960 0 0       0 $maxlen = 0 if ($maxlen eq '');
1961             }
1962 1 50 33     11 if (defined $s && (defined $maxlen && ($maxlen eq '0'))) {
      33        
1963 1         9 my ($i, $istatus) = Compress::Zlib::inflateInit(-WindowBits => -(MAX_WBITS));
1964 1         335 my ($output, $ostatus) = $i->inflate($s);
1965 1 50       42 if (defined $output) {
1966 1         9 $res = $output;
1967             }
1968             }
1969             }
1970             } elsif ($cmd eq 'gzdecode') {
1971 1 50       4 if (scalar @$args >= 1) {
1972 1         4 my $s = $parser->get_strval($$args[0]);
1973 1         4 my $maxlen = '0';
1974 1 50       3 if (scalar @$args > 1) {
1975 0         0 $maxlen = $parser->get_strval($$args[1]);
1976 0 0       0 $maxlen = 0 if ($maxlen eq '');
1977             }
1978 1 50 33     10 if (defined $s && (defined $maxlen && ($maxlen eq '0'))) {
      33        
1979             # http://www.faqs.org/rfcs/rfc1952.html
1980             #
1981 1 50       5 if (substr($s, 0, 3) eq "\x1f\x8b\x08") {
1982 1         2 my $off = 10;
1983 1         2 my $flag = ord(substr($s, 3, 1));
1984 1 50       3 if ($flag > 0 ) {
1985 0 0       0 if ($flag & 4) {
1986 0         0 my ($xlen) = unpack('v', substr($s, $off, 2));
1987 0         0 $off = $off + 2 + $xlen;
1988             }
1989 0 0       0 if ($flag & 8) {
1990 0         0 $off = index($s, "\0", $off) + 1;
1991             }
1992 0 0       0 if ($flag & 16) {
1993 0         0 $off = index($s, "\0", $off) + 1;
1994             }
1995 0 0       0 if ($flag & 2) {
1996 0         0 $off = $off + 2;
1997             }
1998             }
1999 1         3 my $b = substr($s, $off, -8);
2000              
2001 1         4 my ($i, $istatus) = Compress::Zlib::inflateInit(-WindowBits => -(MAX_WBITS));
2002 1         170 my ($output, $ostatus) = $i->inflate($b);
2003 1 50       25 if (defined $output) {
2004 1         6 $res = $output;
2005             }
2006             }
2007             }
2008             }
2009             } elsif ($cmd eq 'gzuncompress') {
2010 1 50       4 if (scalar @$args == 1) {
2011 1         4 my $s = $parser->get_strval($$args[0]);
2012 1 50       3 if (defined $s) {
2013 1         6 $res = Compress::Zlib::uncompress($s);
2014             }
2015             }
2016             } elsif ($cmd eq 'crc32') {
2017 1 50       6 if (scalar @$args == 1) {
2018 1         6 my $s = $parser->get_strval($$args[0]);
2019 1 50       5 if (defined $s) {
2020 1         5 $res = mycrc32b($s);
2021 1         3 $to_num = 1;
2022             }
2023             }
2024             } elsif ($cmd eq 'hash') {
2025 6 50       23 if (scalar @$args >= 2) {
2026 6         30 my $a = $parser->get_strval($$args[0]);
2027 6         20 my $s = $parser->get_strval($$args[1]);
2028 6         11 my $raw = '0';
2029 6 100       17 if (scalar @$args > 2) {
2030 2         8 $raw = $parser->get_strval($$args[2]);
2031             }
2032 6 50 33     37 if (defined $a && defined $s && defined $raw) {
      33        
2033 6         11 my $output;
2034 6 100       27 if ($a eq 'crc32') {
    100          
    100          
    50          
2035 1         5 $output = pack('N', mycrc32a($s));
2036             } elsif ($a eq 'crc32b') {
2037 2         8 $output = pack('N', mycrc32b($s));
2038             } elsif ($a eq 'sha1') {
2039 1         9 $output = sha1($s);
2040             } elsif ($a eq 'md5') {
2041 2         11 $output = md5($s);
2042             }
2043 6 50       19 if (defined $output) {
2044 6 100       22 if ($raw eq '0') {
2045             #$res = sprintf("%x", $output);
2046 4         25 $res = unpack('H*', ''.$output);
2047             } else {
2048 2         6 $res = $output;
2049             }
2050             }
2051             }
2052             }
2053             } elsif ($cmd eq 'rawurldecode') {
2054 0 0       0 if (scalar @$args == 1) {
2055 0         0 my $s = $parser->get_strval($$args[0]);
2056 0 0       0 if (defined $s) {
2057             # no '+'->' ' conversion here
2058 0         0 $res = URI::Escape::uri_unescape($s);
2059             }
2060             }
2061             } elsif ($cmd eq 'rawurlencode') {
2062 1 50       5 if (scalar @$args == 1) {
2063 1         6 my $s = $parser->get_strval($$args[0]);
2064 1 50       3 if (defined $s) {
2065             # no ' '->'+' conversion here
2066             # default safe RFC3986 characters are: "A-Za-z0-9\-\._~"
2067 1         5 $res = URI::Escape::uri_escape($s);
2068             }
2069             }
2070             } elsif ($cmd eq 'urldecode') {
2071 0 0       0 if (scalar @$args == 1) {
2072 0         0 my $s = $parser->get_strval($$args[0]);
2073 0 0       0 if (defined $s) {
2074 0         0 $s =~ s/\+/ /g;
2075 0         0 $res = URI::Escape::uri_unescape($s);
2076             }
2077             }
2078             } elsif ($cmd eq 'urlencode') {
2079 1 50       6 if (scalar @$args == 1) {
2080 1         5 my $s = $parser->get_strval($$args[0]);
2081 1 50       5 if (defined $s) {
2082             # default safe characters are here: "A-Za-z0-9\-\._"
2083             # see: php/ext/standard/url.c
2084 1         6 $res = URI::Escape::uri_escape($s, "^A-Za-z0-9\-\._ ");
2085 1         250 $res =~ s/ /\+/g;
2086             }
2087             }
2088             } elsif ($cmd eq 'trim') {
2089 0 0       0 if (scalar @$args == 1) {
2090 0         0 my $s = $parser->get_strval($$args[0]);
2091 0 0       0 if (defined $s) {
2092 0         0 $res = $s;
2093 0         0 $res =~ s/^\s+|\s+$//g;
2094             }
2095             }
2096             } elsif ($cmd eq 'ltrim') {
2097 0 0       0 if (scalar @$args == 1) {
2098 0         0 my $s = $parser->get_strval($$args[0]);
2099 0 0       0 if (defined $s) {
2100 0         0 $res = $s;
2101 0         0 $res =~ s/^\s+//;
2102             }
2103             }
2104             } elsif (($cmd eq 'rtrim') || ($cmd eq 'chop')) {
2105 0 0       0 if (scalar @$args == 1) {
2106 0         0 my $s = $parser->get_strval($$args[0]);
2107 0 0       0 if (defined $s) {
2108 0         0 $res = $s;
2109 0         0 $res =~ s/\s+$//;
2110             }
2111             }
2112             } elsif ($cmd eq 'stripslashes') {
2113 0 0       0 if (scalar @$args == 1) {
2114 0         0 my $s = $parser->get_strval($$args[0]);
2115 0 0       0 if (defined $s) {
2116 0         0 $res = $s;
2117 0         0 $res =~ s/\\(\'|\"|\\)/$1/g;
2118             }
2119             }
2120             } elsif ($cmd eq 'stripcslashes') {
2121 0 0       0 if (scalar @$args == 1) {
2122 0         0 my $s = $parser->get_strval($$args[0]);
2123 0 0       0 if (defined $s) {
2124 0         0 $res = stripcslashes($s);
2125             }
2126             }
2127             } elsif ($cmd eq 'strpos') {
2128 2 50       8 if (scalar @$args >= 2) {
2129 2         8 my $s = $parser->get_strval($$args[0]);
2130 2         6 my $p = $parser->get_strval($$args[1]);
2131 2         5 my $off = 0;
2132 2 100       6 if (scalar @$args == 3) {
2133 1         4 $off = $parser->get_strval($$args[2]);
2134 1 50       4 $off = 0 if ($off eq '');
2135             }
2136 2 50 33     15 if (defined $s && defined $p) {
2137 2         6 $res = index($s, $p, $off);
2138 2         4 $to_num = 1;
2139             }
2140             }
2141             } elsif ($cmd eq 'substr') {
2142 2 50       8 if (scalar @$args >= 2) {
2143 2         8 my $s = $parser->get_strval($$args[0]);
2144 2         6 my $off = $parser->get_strval($$args[1]);
2145 2 50 33     10 if (defined $s && defined $off) {
2146 2 50       6 $off = 0 if ($off eq '');
2147 2 100       7 if (scalar @$args == 3) {
2148 1         6 my $len = $parser->get_strval($$args[2]);
2149 1 50       5 if (defined $len) {
2150 1 50       3 $len = 0 if ($len eq '');
2151 1         5 $res = substr($s, $off, $len);
2152             }
2153             } else {
2154 1         3 $res = substr($s, $off);
2155             }
2156             }
2157             }
2158             } elsif ($cmd eq 'substr_count') {
2159 1 50       5 if (scalar @$args >= 2) {
2160 1         5 my $s = $parser->get_strval($$args[0]);
2161 1         5 my $p = $parser->get_strval($$args[1]);
2162 1 50       6 if (defined $s) {
2163 1         3 my $sub = $s;
2164 1 50       4 if (scalar @$args > 2) {
2165 1         4 my $off = $parser->get_strval($$args[2]);
2166 1 50       5 if (scalar @$args > 3) {
2167 0         0 my $len = $parser->get_strval($$args[3]);
2168 0         0 $sub = substr($s, $off, $len);
2169             } else {
2170 1         4 $sub = substr($s, $off);
2171             }
2172             }
2173 1         19 $res = () = $sub =~ /\Q$p\E/g;
2174 1         5 $to_num=1;
2175             }
2176             }
2177             } elsif ($cmd eq 'strstr') {
2178 0 0       0 if (scalar @$args >= 2) {
2179 0         0 my $s = $parser->get_strval($$args[0]);
2180 0         0 my $p = $parser->get_strval($$args[1]);
2181 0 0 0     0 if (defined $s && defined $p) {
2182 0         0 my $off = index($s, $p);
2183 0 0       0 if (defined $off) {
2184 0         0 my $before = 0;
2185 0 0       0 if (scalar @$args > 2) {
2186 0         0 $before = $parser->get_strval($$args[2]);
2187             }
2188 0 0       0 if ($before) {
2189 0         0 $res = substr($s, 0, $off);
2190             } else {
2191 0         0 $res = substr($s, $off);
2192             }
2193             }
2194             }
2195             }
2196             } elsif ($cmd eq 'strtr') {
2197             # https://www.php.net/manual/en/function.strtr.php
2198             #
2199             # if from and to have different lengths, the extra characters in
2200             # the longer of the two are ignored. The length of string will be
2201             # the same as the return value's.
2202             #
2203 3 50       11 if (scalar @$args == 3) {
2204 3         11 my $str = $parser->get_strval($$args[0]);
2205 3         9 my $from = $parser->get_strval($$args[1]);
2206 3         9 my $to = $parser->get_strval($$args[2]);
2207 3         6 my $org = $str;
2208              
2209 3 50 33     16 if (defined $str && defined $from && defined $to) {
      33        
2210             # for from > to perl would skip characters missing in 'to',
2211             # for to > from perl would substitue the last char with the full suffix.
2212             #
2213 3 100       12 if (length($from) > length($to)) {
    100          
2214 1         4 $to .= substr($from, length($from)-1);
2215             } elsif (length($to) > length($from)) {
2216 1         4 $from .= substr($to, length($to)-1);
2217             }
2218             # tr needs eval to use variables
2219             #
2220 3         224 eval "\$str =~ tr/\Q$from\E/\Q$to\E/;";
2221 3 50       14 unless ($@) {
2222 3 50       9 $ctx->{log}->($ctx, 'cmd', $cmd, "[%s]->[%s] [%s]->[%s]", $parser->shortstr($from,40), $parser->shortstr($to,40), $parser->shortstr($org,40), $parser->shortstr($str,40)) if $ctx->{log};
2223 3         5 $res = $str;
2224             }
2225             }
2226             }
2227             } elsif ($cmd eq 'strcmp') {
2228             # https://php.net/manual/en/function.strcmp.php
2229             # TODO: perl cmp does just return -1,0,1
2230             #
2231 1 50       5 if (scalar @$args == 2) {
2232 1         5 my $s = $parser->get_strval($$args[0]);
2233 1         4 my $d = $parser->get_strval($$args[1]);
2234 1 50 33     7 if (defined $s && defined $d) {
2235 1         3 $res = $s cmp $d;
2236 1         2 $to_num = 1;
2237             }
2238             }
2239             } elsif ($cmd eq 'strncmp') {
2240             # https://php.net/manual/en/function.strncmp.php
2241             # TODO: perl cmp does just return -1,0,1
2242             #
2243 0 0       0 if (scalar @$args == 3) {
2244 0         0 my $s = $parser->get_strval($$args[0]);
2245 0         0 my $d = $parser->get_strval($$args[1]);
2246 0         0 my $len = $parser->get_strval($$args[2]);
2247 0 0 0     0 if (defined $s && defined $d && defined $len) {
      0        
2248 0 0       0 $len = 0 if ($len eq '');
2249 0         0 $res = substr($s,0,$len) cmp substr($d,0,$len);
2250 0         0 $to_num = 1;
2251             }
2252             }
2253             } elsif ($cmd eq 'strcasecmp') {
2254             # https://php.net/manual/en/function.strcasecmp.php
2255             # TODO: perl cmp does just return -1,0,1
2256             #
2257 0 0       0 if (scalar @$args == 2) {
2258 0         0 my $s = $parser->get_strval($$args[0]);
2259 0         0 my $d = $parser->get_strval($$args[1]);
2260 0 0 0     0 if (defined $s && defined $d) {
2261 0         0 $res = lc($s) cmp lc($d);
2262 0         0 $to_num = 1;
2263             }
2264             }
2265             } elsif ($cmd eq 'strncasecmp') {
2266             # https://php.net/manual/en/function.strncasecmp.php
2267             # TODO: perl cmp does just return -1,0,1
2268             #
2269 1 50       6 if (scalar @$args == 3) {
2270 1         7 my $s = $parser->get_strval($$args[0]);
2271 1         5 my $d = $parser->get_strval($$args[1]);
2272 1         4 my $len = $parser->get_strval($$args[2]);
2273 1 50 33     8 if (defined $s && defined $d && defined $len) {
      33        
2274 1 50       4 $len = 0 if ($len eq '');
2275 1         5 $res = lc(substr($s,0,$len)) cmp lc(substr($d,0,$len));
2276 1         3 $to_num = 1;
2277             }
2278             }
2279             } elsif ($cmd eq 'str_repeat') {
2280             # https://php.net/manual/en/function.str-repeat.php
2281             #
2282 1 50       7 if (scalar @$args == 2) {
2283 1         6 my $s = $parser->get_strval($$args[0]);
2284 1         4 my $m = $parser->get_strval($$args[1]);
2285              
2286 1 50 33     10 if (defined $s && defined $m) {
2287 1 50       6 $m = 0 if ($m eq '');
2288 1 50       5 if ($m > $ctx->{max_repeat}) { # limit maxsize
2289 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$s x $m memory exhaution");
2290             } else {
2291 1         3 $res = $s x $m;
2292             }
2293             }
2294             }
2295             } elsif ($cmd eq 'str_replace') {
2296 4 50       12 if (scalar @$args >= 3) {
2297 4         11 my $pattern = $$args[0];
2298 4         6 my $replacement = $$args[1];
2299 4         8 my $subject = $$args[2];
2300 4         5 my $limit = -1;
2301 4 50       10 if (scalar @$args == 4) {
2302 0         0 $limit = $parser->get_strval($$args[3]);
2303 0 0       0 $limit = 0 if ($limit eq '');
2304             }
2305 4 50 33     22 if (defined $pattern && defined $replacement && defined $subject) {
      33        
2306 4 100       9 if (is_array($subject)) {
    50          
    0          
2307 1         6 my $arr = $parser->{strmap}{$subject};
2308 1         4 my $keys = $arr->get_keys();
2309 1         3 my $newarr = $parser->newarr();
2310            
2311 1         19 foreach my $k (@$keys) {
2312 2         7 my $val = $arr->val($k);
2313 2         10 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $val, 'str', $limit);
2314 2         8 $newarr->set(undef, $r);
2315 2 50       7 if ($limit != -1) {
2316 0         0 $limit -= $cnt;
2317             }
2318             }
2319 1         7 return $newarr->{name};
2320             } elsif (is_strval($subject)) {
2321 3         10 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $subject, 'str', $limit);
2322 3         12 return $r;
2323             } elsif ($subject eq '#null') {
2324 0         0 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $subject, 'str', $limit);
2325 0         0 return $r;
2326             }
2327             }
2328             }
2329             } elsif ($cmd eq 'ereg_replace') {
2330 0 0       0 if (scalar @$args == 3) {
2331 0         0 my $pattern = $parser->get_strval($$args[0]);
2332 0         0 my $replacement = $parser->get_strval($$args[1]);
2333 0         0 my $str = $parser->get_strval($$args[2]);
2334              
2335             # todo: use posix regex here
2336             #
2337 0 0 0     0 if (defined $pattern && defined $replacement && defined $str) {
      0        
2338 0         0 $res = $str;
2339 0         0 $res =~ s/\Q$pattern\E/$replacement/g;
2340 0 0       0 $ctx->{log}->($ctx, 'cmd', $cmd, "[%s]->[%s] %s -> %s", $parser->shortstr($pattern,40), $parser->shortstr($replacement,40), $parser->shortstr($str,40), $parser->shortstr($res,40)) if $ctx->{log};
2341             }
2342             }
2343             } elsif ($cmd eq 'preg_replace') {
2344 7 50       22 if (scalar @$args >= 3) {
2345 7         16 my $pattern = $$args[0];
2346 7         13 my $replacement = $$args[1];
2347 7         10 my $subject = $$args[2];
2348 7         12 my $limit = -1;
2349 7 50       12 if (scalar @$args == 4) {
2350 0         0 $limit = $parser->get_strval($$args[3]);
2351 0 0       0 $limit = 0 if ($limit eq '');
2352             }
2353              
2354             # preg_replace uses pattern '/xxx/' instead of 'xxx'
2355             #
2356 7 50 33     39 if (defined $pattern && defined $replacement && defined $subject) {
      33        
2357 7 100       17 if (is_array($subject)) {
    50          
    0          
2358 1         3 my $arr = $parser->{strmap}{$subject};
2359 1         5 my $keys = $arr->get_keys();
2360 1         7 my $newarr = $parser->newarr();
2361            
2362 1         4 foreach my $k (@$keys) {
2363 2         9 my $val = $arr->val($k);
2364 2         7 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $val, 'preg', $limit);
2365 2         8 $newarr->set(undef, $r);
2366 2 50       13 if ($limit != -1) {
2367 0         0 $limit -= $cnt;
2368             }
2369             }
2370 1         6 return $newarr->{name};
2371             } elsif (is_strval($subject)) {
2372 6         18 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $subject, 'preg', $limit);
2373 6         25 return $r;
2374             } elsif ($subject eq '#null') {
2375 0         0 my ($r, $cnt) = preg_replace_subject($ctx, $pattern, $replacement, $subject, 'preg', $limit);
2376 0         0 return $r;
2377             }
2378             }
2379             }
2380             } elsif ($cmd eq 'preg_quote') {
2381 0 0       0 if (scalar @$args == 1) {
2382 0         0 my $s = $parser->get_strval($$args[0]);
2383 0 0       0 if (defined $s) {
2384             # escape: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -
2385 0         0 $s =~ s/%([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-])/\\$1/ge;
  0         0  
2386 0         0 $res = $s;
2387             }
2388             }
2389             } elsif ($cmd eq 'md5') {
2390 1 50       5 if (scalar @$args == 1) {
2391 1         16 my $s = $parser->get_strval($$args[0]);
2392 1 50       5 if (defined $s) {
2393 1         10 $res = md5_hex($s);
2394             }
2395             }
2396             } elsif ($cmd eq 'sha1') {
2397 1 50       5 if (scalar @$args == 1) {
2398 1         5 my $s = $parser->get_strval($$args[0]);
2399 1 50       6 if (defined $s) {
2400 1         11 $res = sha1_hex($s);
2401             }
2402             }
2403             } elsif ($cmd eq 'levenshtein') {
2404 0 0       0 if (scalar @$args == 2) {
2405 0         0 my $s1 = $parser->get_strval($$args[0]);
2406 0         0 my $s2 = $parser->get_strval($$args[1]);
2407 0 0 0     0 if (defined $s1 && defined $s2) {
2408 0         0 $res = levenshtein($s1, $2);
2409             }
2410             }
2411             } elsif ($cmd eq 'str_split') {
2412 0 0       0 if (scalar @$args >= 1) {
2413 0         0 my $s1 = $parser->get_strval($$args[0]);
2414 0         0 my $s2 = '1';
2415 0 0       0 if (scalar @$args == 2) {
2416 0         0 $s2 = $parser->get_strval($$args[1]);
2417             }
2418 0 0 0     0 if (defined $s1 && defined $s2) {
2419 0         0 my $arr = $parser->newarr();
2420             # don't include delimter in split result
2421 0         0 my @parts = $s1 =~ /.{1,$s2}/g;
2422              
2423 0         0 foreach my $p (@parts) {
2424 0 0       0 $ctx->{log}->($ctx, 'cmd', $cmd, "[$s2] part: $p") if $ctx->{log};
2425 0         0 my $k = $parser->setstr($p);
2426 0         0 $arr->set(undef, $k);
2427             }
2428 0         0 return $arr->{name};
2429             }
2430             }
2431             } elsif ($cmd eq 'preg_split') {
2432 1 50       7 if (scalar @$args >= 2) {
2433 1         5 my $s1 = $parser->get_strval($$args[0]);
2434 1         6 my $s2 = $parser->get_strval($$args[1]);
2435 1         2 my $limit = '-1';
2436 1         3 my $flags = 0;
2437 1 50       4 if (scalar @$args > 2) {
2438 0         0 $limit = $parser->get_strval($$args[2]);
2439 0 0       0 $limit = 0 if ($limit eq '');
2440             }
2441 1 50       6 if (scalar @$args > 3) {
2442 0         0 $flags = $parser->get_strval($$args[3]);
2443             }
2444 1 50 33     11 if (defined $s1 && defined $s2 && ($flags == 0)) {
      33        
2445 1         5 my $arr = $parser->newarr();
2446 1         7 my ($pattern) = $s1 =~ /^\/(.*)\/$/;
2447 1         27 my @parts = split /$pattern/, $s2, $limit;
2448              
2449 1         4 foreach my $p (@parts) {
2450 3 50       9 $ctx->{log}->($ctx, 'cmd', $cmd, "[$s2] '$s1' part: $p") if $ctx->{log};
2451 3         9 my $k = $parser->setstr($p);
2452 3         8 $arr->set(undef, $k);
2453             }
2454 1         6 return $arr->{name};
2455             }
2456             }
2457             } elsif ($cmd eq 'explode') {
2458 0 0       0 if (scalar @$args >= 2) {
2459 0         0 my $s1 = $parser->get_strval($$args[0]);
2460 0         0 my $s2 = $parser->get_strval($$args[1]);
2461 0         0 my $limit = '-1';
2462 0 0       0 if (scalar @$args == 3) {
2463 0         0 $limit = $parser->get_strval($$args[2]);
2464             }
2465 0 0 0     0 if (defined $s1 && defined $s2) {
2466 0         0 my $arr = $parser->newarr();
2467 0         0 my @parts = split /\Q$s1\E/, $s2, $limit;
2468              
2469 0         0 foreach my $p (@parts) {
2470 0 0       0 $ctx->{log}->($ctx, 'cmd', $cmd, "[$s2] '$s1': part: $p") if $ctx->{log};
2471 0         0 my $k = $parser->setstr($p);
2472 0         0 $arr->set(undef, $k);
2473             }
2474 0         0 return $arr->{name};
2475             }
2476             }
2477             } elsif (($cmd eq 'implode') || ($cmd eq 'join')) {
2478             # https://php.net/manual/en/function.implode.php
2479             # 'join' is an alias for implode()
2480             #
2481 4 50       16 if (scalar @$args >= 1) {
2482 4         10 my $basestr = $$args[0];
2483 4         9 my $glue = '';
2484              
2485 4 50       10 if (scalar @$args == 2) {
2486 4 100       15 if (is_array($$args[0])) {
2487 1         15 $ctx->{warn}->($ctx, 'cmd', $cmd, "reversed arguments $$args[0] $$args[1]");
2488 1         53 $basestr = $$args[0];
2489 1         6 $glue = $parser->get_strval($$args[1]);
2490             } else {
2491 3         12 $glue = $parser->get_strval($$args[0]);
2492 3         9 $basestr = $$args[1];
2493             }
2494             }
2495 4 50 33     21 if (defined $glue && defined $basestr) {
2496 4 100       12 if (is_array($basestr)) {
2497 2         5 my $arr = $parser->{strmap}{$basestr};
2498 2         9 my $keys = $arr->get_keys();
2499              
2500 2         7 my @vals = ();
2501 2         6 my $failed = 0;
2502 2         5 foreach my $k (@$keys) {
2503 4         10 my $val = $arr->val($k);
2504 4 50       17 if (is_strval($val)) {
2505 4         15 push (@vals, $parser->{strmap}->{$val});
2506             } else {
2507 0         0 my $v = $ctx->exec_statement($val);
2508 0 0       0 if (is_strval($v)) {
2509 0         0 push (@vals, $parser->{strmap}->{$v});
2510             } else {
2511 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$basestr: key $k not str ($val -> $v)");
2512 0         0 $failed = 1;
2513 0         0 last;
2514             }
2515             }
2516             }
2517 2 50       18 $res = join($glue, @vals) unless ($failed);
2518             }
2519             }
2520             }
2521             } elsif ($cmd eq 'range') {
2522             # https://www.php.net/manual/en/function.range.php
2523             #
2524 5 50       19 if (scalar @$args >= 2) {
2525 5         22 my $s1 = $parser->get_strval($$args[0]);
2526 5         26 my $s2 = $parser->get_strval($$args[1]);
2527 5         17 my $step = '1';
2528 5 50       17 if (scalar @$args == 3) {
2529 0         0 $step = $parser->get_strval($$args[2]);
2530 0 0       0 $step = 0 if ($step eq '');
2531             }
2532 5 50 33     29 if (defined $s1 && defined $s2) {
2533 5         18 my $arr = $parser->newarr();
2534 5         10 my @parts;
2535              
2536             # https://perldoc.perl.org/perlop#Range-Operators
2537             # If the initial value specified isn't part of a magical increment sequence
2538             # (that is, a non-empty string matching /^[a-zA-Z]*[0-9]*\z/), only the
2539             # initial value will be returned.
2540 5 100 66     47 if (($s1 =~ /^[a-zA-Z0-9]*$/) && ($s2 =~ /^[a-zA-Z0-9]*$/)) {
2541 4         25 @parts = ($s1 .. $s2);
2542             } else {
2543 1         5 @parts = map { chr } (ord($s1) .. ord($s2));
  2         7  
2544             }
2545              
2546 5         15 foreach my $p (@parts) {
2547 8 50       21 $ctx->{log}->($ctx, 'cmd', $cmd, "['$s1', '$s2']: part: $p") if $ctx->{log};
2548 8         22 my $k = $parser->setstr($p);
2549 8         25 $arr->set(undef, $k);
2550             }
2551 5         23 return $arr->{name};
2552             }
2553             }
2554             } elsif (($cmd eq 'count') || ($cmd eq 'sizeof')) {
2555             # https://php.net/manual/en/function.count.php
2556             # todo: support arg2: COUNT_NORMAL/COUNT_RECURSIVE
2557             #
2558 2 50       11 if (scalar @$args == 1) {
2559 2         6 my $basestr = $$args[0];
2560 2 50       10 if (defined $basestr) {
2561 2 100       9 if (is_array($basestr)) {
2562 1         4 my $arr = $parser->{strmap}{$basestr};
2563 1         4 my $keys = $arr->get_keys();
2564 1         4 $res = scalar @$keys;
2565 1         4 $to_num = 1;
2566             }
2567             }
2568             }
2569             } elsif (($cmd eq 'htmlspecialchars') || ($cmd eq 'htmlentities')) {
2570 0 0       0 if (scalar @$args >= 1) {
2571 0         0 my $s = $parser->get_strval($$args[0]);
2572 0 0       0 if (defined $s) {
2573 0         0 $res = HTML::Entities::encode_entities($s);
2574             }
2575             }
2576             } elsif (($cmd eq 'htmlspecialchars_decode') || ($cmd eq 'html_entity_decode')) {
2577             # just support 1 argument version without flags & charset for now
2578             #
2579 0 0       0 if (scalar @$args == 1) {
2580 0         0 my $s = $parser->get_strval($$args[0]);
2581 0 0       0 if (defined $s) {
2582 0         0 $res = HTML::Entities::decode_entities($s);
2583             }
2584             }
2585             } elsif ($cmd eq 'pack') {
2586 2 50       8 if (scalar @$args >= 1) {
2587 2         10 my $s = $parser->get_strval($$args[0]);
2588 2         11 my @param = ();
2589 2         3 my $i;
2590              
2591 2 50       8 if (defined $s) {
2592 2         14 for ($i=1; $i < scalar @$args; $i++) {
2593 4         14 my $p = $parser->get_strval($args->[$i]);
2594 4 50       14 last unless (defined $p);
2595 4         12 push(@param, $p);
2596             }
2597 2 50       24 if ($i == scalar @$args) {
2598 2         4 eval { $res = pack($s, @param); };
  2         15  
2599 2 50       9 if ($@) {
2600 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "bad param $s");
2601             }
2602             }
2603             }
2604             }
2605             } elsif ($cmd eq 'unpack') {
2606 1 50       6 if (scalar @$args == 2) {
2607 1         5 my $s = $parser->get_strval($$args[0]);
2608 1         4 my $d = $parser->get_strval($$args[1]);
2609              
2610 1 50 33     8 if (defined $s && defined $d) {
2611 1         3 my @values;
2612 1         2 eval { @values = unpack($s, $d); };
  1         5  
2613 1 50       4 if ($@) {
2614 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "bad param $s");
2615             }
2616              
2617 1         5 my $arr = $parser->newarr();
2618              
2619 1         4 foreach my $p (@values) {
2620 2 50       6 $ctx->{log}->($ctx, 'cmd', $cmd, "[$s]: $p") if $ctx->{log};
2621 2         5 my $k = $parser->setstr($p);
2622 2         7 $arr->set(undef, $k);
2623             }
2624 1         5 return $arr->{name};
2625             }
2626             }
2627             } elsif ($cmd eq 'sprintf') {
2628 1 50       5 if (scalar @$args >= 1) {
2629 1         7 my $s = $parser->get_strval($$args[0]);
2630 1         4 my @param = ();
2631 1         2 my $i;
2632              
2633 1 50       4 if (defined $s) {
2634 1         5 for ($i=1; $i < scalar @$args; $i++) {
2635 2         6 my $p = $parser->get_strval($args->[$i]);
2636 2 50       5 last unless (defined $p);
2637 2         6 push(@param, $p);
2638             }
2639 1 50       4 if ($i == scalar @$args) {
2640 1         3 eval { $res = sprintf($s, @param); };
  1         8  
2641 1 50       6 if ($@) {
2642 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "bad param $s");
2643             }
2644             }
2645             }
2646             }
2647             } elsif ($cmd eq 'array_push') {
2648             # https://php.net/manual/en/function.array-push.php
2649             #
2650 2 50       16 if (scalar @$args >= 1) {
2651 2         6 my $var = $$args[0]; # ref var
2652 2         6 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2653              
2654 2 50       8 if ($is_valid) {
2655 2 50 66     11 if (!defined $val || is_null($val) || is_array($val)) {
      66        
2656 2         5 my $arr;
2657 2 100 66     24 if (defined $val && is_array($val)) {
2658 1         5 $arr = $parser->{strmap}{$val};
2659 1         6 $arr = $arr->copy(); # recursive copy
2660             } else {
2661 1         10 $arr = $parser->newarr();
2662             }
2663 2         7 for (my $i=1; $i < scalar @$args; $i++) {
2664 2         9 $arr->set(undef, $$args[$i]);
2665             }
2666 2         10 set_refvar_val($ctx, $var, $arr->{name});
2667 2         7 my $keys = $arr->get_keys();
2668              
2669 2         7 $res = scalar keys @$keys; # returns elem count
2670 2         7 $to_num = 1;
2671             }
2672             }
2673             }
2674             } elsif ($cmd eq 'array_pop') {
2675             # https://php.net/manual/en/function.array-pop.php
2676             #
2677 2 50       10 if (scalar @$args == 1) {
2678 2         5 my $var = $$args[0]; # ref var
2679 2         7 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2680              
2681 2 50       6 if ($is_valid) {
2682 2 50 33     23 if (!defined $val || is_array($val)) {
2683 2 50 33     22 if (defined $val && is_array($val)) {
2684 2         8 my $arr = $parser->{strmap}{$val};
2685 2         9 my $keys = $arr->get_keys();
2686 2 100       18 if (scalar @$keys == 0) {
2687 1         3 $res = '#null';
2688             } else {
2689 1         7 my $k = pop @$keys;
2690 1         4 $res = $arr->val($k);
2691 1         7 $arr = $arr->copy($keys);
2692 1         7 set_refvar_val($ctx, $var, $arr->{name});
2693             }
2694             } else {
2695 0         0 $res = '#null';
2696             }
2697 2         18 return $res;
2698             }
2699             }
2700             }
2701             } elsif ($cmd eq 'array_unshift') {
2702             # https://php.net/manual/en/function.array-unshift.php
2703             #
2704 2 50       9 if (scalar @$args >= 1) {
2705 2         5 my $var = $$args[0]; # ref var
2706 2         6 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2707              
2708 2 50       10 if ($is_valid) {
2709 2 50 66     20 if (!defined $val || is_null($val) || is_array($val)) {
      66        
2710 2         15 my $newarr = $parser->newarr();
2711              
2712 2         13 for (my $i=1; $i < scalar @$args; $i++) {
2713 2         21 $newarr->set(undef, $$args[$i]); # prepend new elems
2714             }
2715 2 100 66     22 if (defined $val && is_array($val)) {
2716 1         8 my $arr = $parser->{strmap}{$val};
2717 1         7 my $keys = $arr->get_keys();
2718              
2719 1         4 foreach my $k (@$keys) {
2720 1         4 my $oldval = $arr->val($k);
2721 1 50       4 if (is_int_index($k)) {
2722 1         8 $newarr->set(undef, $oldval); # renumber int key
2723             } else {
2724 0         0 $newarr->set($k, $oldval);
2725             }
2726             }
2727             }
2728 2         11 set_refvar_val($ctx, $var, $newarr->{name});
2729              
2730 2         11 my $keys = $newarr->get_keys();
2731 2         6 $res = scalar keys @$keys; # returns elem count
2732 2         7 $to_num = 1;
2733             }
2734             }
2735             }
2736             } elsif ($cmd eq 'array_shift') {
2737             # https://php.net/manual/en/function.array-shift.php
2738             #
2739 2 50       11 if (scalar @$args == 1) {
2740 2         16 my $var = $$args[0]; # ref var
2741 2         7 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2742              
2743 2 50       6 if ($is_valid) {
2744 2 50 33     10 if (!defined $val || is_array($val)) {
2745 2 50 33     13 if (defined $val && is_array($val)) {
2746 2         7 my $arr = $parser->{strmap}{$val};
2747 2         7 my $keys = $arr->get_keys();
2748 2 100       12 if (scalar @$keys == 0) {
2749 1         3 $res = '#null';
2750             } else {
2751 1         3 my $k = shift @$keys;
2752 1         12 $res = $arr->val($k);
2753              
2754 1         6 my $newarr = $parser->newarr();
2755 1         5 foreach my $k (@$keys) {
2756 1         3 my $oldval = $arr->val($k);
2757 1 50       5 if (is_int_index($k)) {
2758 1         4 $newarr->set(undef, $oldval); # renumber int key
2759             } else {
2760 0         0 $newarr->set($k, $oldval);
2761             }
2762             }
2763 1         5 set_refvar_val($ctx, $var, $newarr->{name});
2764             }
2765             } else {
2766             }
2767             } else {
2768 0         0 $res = '#null';
2769             }
2770 2         6 return $res;
2771             }
2772             }
2773             } elsif ($cmd eq 'each') {
2774             # https://php.net/manual/en/function.each.php
2775             #
2776 5 50       20 if (scalar @$args == 1) {
2777 5         15 my $var = $$args[0]; # ref var
2778 5         13 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2779              
2780 5 50       19 if ($is_valid) {
2781 5 50 33     37 if (defined $val && is_array($val)) {
2782 5         16 my $arr = $parser->{strmap}{$val};
2783 5         17 my $pos = $arr->get_pos();
2784 5         14 my $keys = $arr->get_keys();
2785 5 100       22 if ($pos >= scalar @$keys) {
2786 1         2 $res = 0;
2787 1         3 $to_num = 1;
2788             } else {
2789 4         32 my $k = $keys->[$pos];
2790 4         12 my $v = $arr->val($k);
2791 4         14 my $newarr = $parser->newarr();
2792 4 50       15 if (is_int_index($k)) {
2793 4         13 $k = $parser->setnum($k);
2794             }
2795 4         15 $newarr->set(undef, $k);
2796 4         9 $newarr->set(undef, $v);
2797 4         47 $arr->set_pos($pos+1);
2798 4         20 return $newarr->{name};
2799             }
2800             } else {
2801 0         0 $res = 0;
2802 0         0 $to_num = 1;
2803             }
2804             }
2805             }
2806             } elsif ($cmd eq 'reset') {
2807             # https://php.net/manual/en/function.reset.php
2808             #
2809 1 50       6 if (scalar @$args == 1) {
2810 1         3 my $var = $$args[0]; # ref var
2811 1         5 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2812              
2813 1 50       5 if ($is_valid) {
2814 1 50 33     19 if (defined $val && is_array($val)) {
2815 1         4 my $arr = $parser->{strmap}{$val};
2816 1         5 my $keys = $arr->get_keys();
2817 1         5 $arr->set_pos(0);
2818 1 50       3 if (scalar @$keys == 0) {
2819 0         0 $res = 0;
2820 0         0 $to_num = 1;
2821             } else {
2822 1         3 my $k = shift @$keys;
2823 1         4 $res = $arr->val($k);
2824 1         5 return $res;
2825             }
2826             } else {
2827 0         0 $res = 0;
2828 0         0 $to_num = 1;
2829             }
2830             }
2831             }
2832             } elsif ($cmd eq 'next') {
2833             # https://php.net/manual/en/function.next.php
2834             #
2835 4 50       13 if (scalar @$args == 1) {
2836 4         8 my $var = $$args[0]; # ref var
2837 4         10 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2838              
2839 4 50       12 if ($is_valid) {
2840 4 50 33     16 if (defined $val && is_array($val)) {
2841 4         9 my $arr = $parser->{strmap}{$val};
2842 4         14 my $pos = $arr->get_pos();
2843 4         9 my $keys = $arr->get_keys();
2844 4 100       13 if (($pos+1) >= scalar @$keys) {
2845 1         3 $res = 0;
2846 1         3 $to_num = 1;
2847             } else {
2848 3         6 my $k = $keys->[$pos+1];
2849 3         8 $res = $arr->val($k);
2850 3         10 $arr->set_pos($pos+1);
2851 3         10 return $res;
2852             }
2853             } else {
2854 0         0 $res = 0;
2855 0         0 $to_num = 1;
2856             }
2857             }
2858             }
2859             } elsif ($cmd eq 'prev') {
2860             # https://php.net/manual/en/function.prev.php
2861             #
2862 1 50       6 if (scalar @$args == 1) {
2863 1         2 my $var = $$args[0]; # ref var
2864 1         4 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2865              
2866 1 50       4 if ($is_valid) {
2867 1 50 33     7 if (defined $val && is_array($val)) {
2868 1         7 my $arr = $parser->{strmap}{$val};
2869 1         8 my $pos = $arr->get_pos();
2870 1         5 my $keys = $arr->get_keys();
2871 1 50 33     19 if (($pos == 0) || (scalar @$keys == 0)) {
2872 0         0 $res = 0;
2873 0         0 $to_num = 1;
2874             } else {
2875 1         4 my $k = $keys->[$pos-1];
2876 1         4 $res = $arr->val($k);
2877 1         6 $arr->set_pos($pos-1);
2878 1         4 return $res;
2879             }
2880             } else {
2881 0         0 $res = 0;
2882 0         0 $to_num = 1;
2883             }
2884             }
2885             }
2886             } elsif ($cmd eq 'end') {
2887             # https://php.net/manual/en/function.end.php
2888             #
2889 1 50       6 if (scalar @$args == 1) {
2890 1         5 my $var = $$args[0]; # ref var
2891 1         39 my ($is_valid, $val) = get_refvar_val($ctx, $var);
2892              
2893 1 50       16 if ($is_valid) {
2894 1 50 33     12 if (defined $val && is_array($val)) {
2895 1         5 my $arr = $parser->{strmap}{$val};
2896 1         5 my $keys = $arr->get_keys();
2897 1 50       13 if (scalar @$keys == 0) {
2898 0         0 $res = 0;
2899 0         0 $to_num = 1;
2900             } else {
2901 1         4 my $pos = scalar @$keys - 1;
2902 1         3 my $k = $keys->[$pos];
2903 1         4 $res = $arr->val($k);
2904 1         8 $arr->set_pos($pos);
2905 1         4 return $res;
2906             }
2907             } else {
2908 0         0 $res = 0;
2909 0         0 $to_num = 1;
2910             }
2911             }
2912             }
2913             } elsif (($cmd eq 'current') || ($cmd eq 'pos')) {
2914             # https://php.net/manual/en/function.current.php
2915             #
2916 1 50       4 if (scalar @$args == 1) {
2917 1         4 my $var = $$args[0];
2918              
2919 1 50 33     4 unless ($ctx->{infunction} && !$ctx->{incall}) {
2920 1 50       4 if (is_array($var)) {
2921 1         3 my $arr = $parser->{strmap}{$var};
2922 1         4 my $pos = $arr->get_pos();
2923 1         7 my $keys = $arr->get_keys();
2924 1 50       20 if ($pos >= scalar @$keys) {
2925 0         0 $res = 0;
2926 0         0 $to_num = 1;
2927             } else {
2928 1         7 my $k = $keys->[$pos];
2929 1         6 $res = $arr->val($k);
2930 1         5 return $res;
2931             }
2932             } else {
2933 0         0 $res = 0;
2934 0         0 $to_num = 1;
2935             }
2936             }
2937             }
2938             } elsif ($cmd eq 'key') {
2939             # https://php.net/manual/en/function.key.php
2940             #
2941 0 0       0 if (scalar @$args == 1) {
2942 0         0 my $var = $$args[0];
2943              
2944 0 0 0     0 unless ($ctx->{infunction} && !$ctx->{incall}) {
2945 0 0       0 if (is_array($var)) {
2946 0         0 my $arr = $parser->{strmap}{$var};
2947 0         0 my $pos = $arr->get_pos();
2948 0         0 my $keys = $arr->get_keys();
2949 0 0       0 if ($pos >= scalar @$keys) {
2950 0         0 $res = '#null';
2951 0         0 return $res;
2952             } else {
2953 0         0 my $k = $keys->[$pos];
2954 0 0       0 if (is_int_index($k)) {
2955 0         0 $k = $parser->setnum($k);
2956             }
2957 0         0 return $k;
2958             }
2959             } else {
2960 0         0 $res = '#null';
2961 0         0 return $res;
2962             }
2963             }
2964             }
2965             } elsif ($cmd eq 'array_map') {
2966             # https://php.net/manual/en/function.array-map.php
2967             #
2968 2 50       12 if (scalar @$args == 2) {
2969 2         8 my $name = $$args[0];
2970 2         4 my $param = $$args[1];
2971 2 50 33     20 if ((is_strval($name) || is_variable($name)) && is_array($param)) {
      33        
2972 2         6 my $arr = $parser->{strmap}{$param};
2973 2         9 my $keys = $arr->get_keys();
2974              
2975 2 50       9 if (is_strval($name)) {
2976 2         8 $name = $parser->{strmap}{$name};
2977             }
2978 2 50       9 if (defined $keys) {
2979 2         8 my $newarr = $parser->newarr();
2980 2         8 foreach my $k (@$keys) {
2981 3         16 my $val = $arr->val($k);
2982 3         14 my $c = $parser->setcall($name, [$val]);
2983 3         13 my $v = $ctx->exec_statement($c);
2984 3         20 $newarr->set($k, $v);
2985             }
2986 2         15 return $newarr->{name};
2987             }
2988             }
2989             }
2990             } elsif ($cmd eq 'array_walk') {
2991             # https://php.net/manual/en/function.array-walk.php
2992             #
2993 2 50       9 if (scalar @$args >= 2) {
2994 2         5 my $var = $$args[0]; # ref var
2995 2         5 my $name = $$args[1]; # callable
2996 2         3 my $arg;
2997 2 100       7 if (is_strval($name)) {
2998 1         4 $name = $parser->{strmap}->{$name};
2999             }
3000 2 50       7 if (scalar @$args > 2) {
3001 0         0 $arg = $$args[2];
3002             }
3003 2         8 my ($is_valid, $val) = get_refvar_val($ctx, $var);
3004              
3005 2 50       15 if ($is_valid) {
3006 2 50 33     9 if (defined $val && is_array($val)) {
3007             # todo: need only to create new array if handler has ref-param
3008             #
3009 2         6 my $arr = $parser->{strmap}{$val};
3010 2         13 my $keys = $arr->get_keys();
3011 2         14 my $newarr = $parser->newarr();
3012 2         6 foreach my $k (@$keys) {
3013 4         10 my $oldval = $arr->val($k);
3014 4         12 my $k2 = $k;
3015 4 50       9 if (is_int_index($k)) {
3016 4         12 $k2 = $parser->setnum($k);
3017             }
3018 4         11 my $v = $var.'__walktmp';
3019 4         15 $ctx->setvar($v, $oldval, 1);
3020 4 50       16 my $c = $parser->setcall($name, defined $arg ? [$v, $k2, $arg] : [$v, $k2]);
3021 4         13 my $r = $ctx->exec_statement($c, 1);
3022 4         13 my $n = $ctx->getvar($v, 1);
3023 4         14 $newarr->set($k, $n);
3024             }
3025 2         8 set_refvar_val($ctx, $var, $newarr->{name});
3026             }
3027 2         4 $res = 1;
3028 2         5 $to_num = 1;
3029             }
3030             }
3031             } elsif ($cmd eq 'array_rand') {
3032             # https://php.net/manual/en/function.array-rand.php
3033             #
3034 2 50       11 if (scalar @$args >= 1) {
3035 2         6 my $param = $$args[0];
3036 2         4 my $num = 1;
3037 2 100       5 if (scalar @$args == 2) {
3038 1         6 $num = $parser->get_strval($$args[1]);
3039             }
3040 2 50 33     9 unless ($ctx->{infunction} && !$ctx->{incall}) {
3041             # don't precalc rand() in function body
3042             #
3043 2 50       7 if (is_array($param)) {
3044 2         5 my $arr = $parser->{strmap}{$param};
3045 2         7 my $keys = $arr->get_keys();
3046 2 50 33     16 if (!defined $keys || ($num > scalar @$keys)) {
    100          
3047 0         0 $res = 0;
3048 0         0 $to_num = 1;
3049             } elsif ($num == 1) {
3050 1         6 my $i = int(rand(scalar @$keys));
3051 1         4 my $k = $keys->[$i]; # return key
3052 1 50       4 if (is_int_index($k)) {
3053 0         0 $res = $k;
3054 0         0 $to_num = 1;
3055             } else {
3056 1         4 $res = $parser->get_strval($k);
3057             }
3058             } else {
3059             # keep random keys uniq & in order
3060 1         51 my @randidx = shuffle(0 .. $#$keys);
3061 1         11 @randidx = sort { $a <=> $b } @randidx[0..$num-1];
  1         4  
3062              
3063 1         7 my $newarr = $parser->newarr();
3064 1         6 foreach (my $i=0; $i < $num; $i++) {
3065 2         5 my $k = $keys->[$randidx[$i]];
3066 2 50       6 if (is_int_index($k)) {
3067 2         23 $k = $parser->setnum($k);
3068             }
3069 2         7 $newarr->set(undef, $k); # append key
3070             }
3071 1         5 return $newarr->{name};
3072             }
3073             }
3074             }
3075             }
3076             } elsif ($cmd eq 'round') {
3077 1 50       6 if (scalar @$args >= 1) {
3078 1         6 my $s = $parser->get_strval($$args[0]);
3079 1         3 my $precision = 0;
3080 1 50       4 if (scalar @$args == 2) {
3081 1         4 $precision = $parser->get_strval($$args[1]);
3082             }
3083 1 50 33     9 if (defined $s && defined $precision) {
3084 1 50       4 if ($precision == 0) {
3085 0         0 $res = int($s + 0.5);
3086             } else {
3087 1         14 $res = sprintf "%0.*f", $precision, $s;
3088             }
3089 1         2 $to_num = 1;
3090             }
3091             }
3092             } elsif (($cmd eq 'rand') || ($cmd eq 'mt_rand')) {
3093 0         0 my $min = 0;
3094 0         0 my $max = 32767;
3095 0 0       0 if (scalar @$args == 2) {
3096 0         0 $min = $parser->get_strval($$args[0]);
3097 0         0 $max = $parser->get_strval($$args[1]);
3098             }
3099 0 0 0     0 if (defined $min && defined $max) {
3100             # don't precalc rand() in function body
3101             #
3102 0 0 0     0 unless ($ctx->{infunction} && !$ctx->{incall}) {
3103 0         0 my $range = int($max) - int($min);
3104 0         0 $res = int(rand($range)) + int($min);
3105 0         0 $to_num = 1;
3106             }
3107             }
3108             } elsif ($cmd eq 'empty') {
3109             # https://php.net/manual/en/function.empty.php
3110             # (https://php.net/manual/en/types.comparisons.php)
3111             #
3112 15 50       64 if (scalar @$args >= 1) {
3113 15         31 my $val = $$args[0];
3114 15 100       57 if (is_array($val)) {
    100          
    100          
    100          
    50          
3115 2         5 my $arr = $parser->{strmap}{$val};
3116 2 100       8 $res = $arr->empty() ? 1 : 0;
3117 2         4 $to_num = 1;
3118             } elsif (is_null($val)) {
3119 3         7 $res = 1;
3120 3         7 $to_num = 1;
3121             } elsif (is_strval($val)) {
3122 3         10 my $s = $parser->get_strval($val);
3123 3 50 66     30 if (!defined $s) {
    50          
    100          
3124 0         0 $res = 1;
3125             } elsif ($s eq '0') {
3126 0         0 $res = 1;
3127             } elsif (($val =~ /^(\#str\d+)$/) && ($s eq '')) {
3128 1         5 $res = 1;
3129             } else {
3130 2         6 $res = 0;
3131             }
3132 3         6 $to_num = 1;
3133             } elsif (is_variable($val)) {
3134 1         6 my $v = $ctx->getvar($val);
3135             #$ctx->{log}->($ctx, 'cmd', $cmd, "$val -> $v: (tainted: %s)", $ctx->{tainted}) if $ctx->{log};
3136 1 50       8 if (!defined $v) {
    50          
3137 0 0 0     0 unless (&parsing_func($ctx) || $ctx->{tainted}) {
3138 0 0       0 unless ($ctx->{skipundef}) {
3139 0         0 $res = 1;
3140 0         0 $to_num = 1;
3141             }
3142             }
3143             } elsif ($v eq '#unresolved') {
3144 1         8 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val is #unresolved");
3145             }
3146             } elsif ($val =~ /^(\#elem\d+)$/) {
3147 6         28 my ($v, $i) = @{$parser->{strmap}->{$val}};
  6         23  
3148 6         31 my ($basevar, $has_index, $idxstr) = $ctx->resolve_variable($val, 0);
3149 6 50       18 if (defined $basevar) {
3150 6 50       15 if ($has_index) {
3151 6         19 my $basestr = $ctx->exec_statement($basevar, 0);
3152 6 100 66     29 if (defined $basestr && is_array($basestr) && defined $idxstr) {
    50 66        
      0        
      33        
3153 2 50       8 $idxstr = $parser->setstr('') if is_null($idxstr); # null maps to '' array index
3154 2         8 my $arr = $parser->{strmap}{$basestr};
3155 2         9 my $arrval = $arr->get($idxstr);
3156 2 50       34 if (defined $arrval) {
    100          
3157 0         0 my $s = $parser->get_strval($arrval);
3158 0 0 0     0 if (!defined $s) {
    0          
    0          
3159 0         0 $res = 1;
3160             } elsif ($s eq '0') {
3161 0         0 $res = 1;
3162             } elsif (($arrval =~ /^(\#str\d+)$/) && ($s eq '')) {
3163 0         0 $res = 1;
3164             } else {
3165 0         0 $res = 0;
3166             }
3167             } elsif (!$ctx->is_superglobal($basevar)) {
3168 1         3 $res = 1;
3169             }
3170 2         9 $to_num = 1;
3171             } elsif (!defined $basestr && (!$ctx->is_superglobal($basevar) || ($basevar =~ /^\$GLOBALS$/))) {
3172 0 0       0 unless ($ctx->{tainted}) {
3173 0 0       0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val %s[%s] is #unresolved", $basevar, defined $i ? $i : '');
3174             }
3175             }
3176             } else {
3177 0         0 my $v = $ctx->getvar($basevar);
3178 0 0       0 if (!defined $v) {
    0          
3179 0 0 0     0 unless (&parsing_func($ctx) || $ctx->{tainted}) {
3180 0 0       0 unless ($ctx->{skipundef}) {
3181 0         0 $res = 1;
3182 0         0 $to_num = 1;
3183             }
3184             }
3185             } elsif ($v eq '#unresolved') {
3186 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val is #unresolved");
3187             }
3188             }
3189             }
3190             }
3191             }
3192             } elsif ($cmd eq 'is_array') {
3193             # https://www.php.net/manual/en/function.is-array.php
3194             #
3195 3 50       10 if (scalar @$args == 1) {
3196 3         8 my $val = $$args[0];
3197 3 100       10 if (is_array($val)) {
    100          
    50          
    0          
3198 1         9 $res = 1;
3199 1         3 $to_num = 1;
3200             } elsif ($ctx->is_superglobal($val)) {
3201 1         3 $res = 1;
3202 1         2 $to_num = 1;
3203             } elsif (is_null($val)) {
3204 1         8 $res = 0;
3205 1         3 $to_num = 1;
3206             } elsif (is_strval($val)) {
3207 0         0 $res = 0;
3208 0         0 $to_num = 1;
3209             }
3210             }
3211             } elsif ($cmd eq 'is_null') {
3212             # https://php.net/manual/en/function.is-null.php (similar to $a === null)
3213             # (https://php.net/manual/en/types.comparisons.php)
3214             #
3215 15 50       48 if (scalar @$args >= 1) {
3216 15         36 my $val = $$args[0];
3217 15 100       50 if (is_array($val)) {
    100          
    100          
    100          
    100          
3218 2         4 $res = 0;
3219 2         6 $to_num = 1;
3220             } elsif (is_null($val)) {
3221 3         7 $res = 1;
3222 3         6 $to_num = 1;
3223             } elsif (is_strval($val)) {
3224 2         11 my $s = $parser->get_strval($val);
3225 2 50       7 if (!defined $s) {
3226 0         0 $res = 1;
3227             } else {
3228 2         7 $res = 0;
3229             }
3230 2         3 $to_num = 1;
3231             } elsif (is_variable($val)) {
3232 4         14 my $v = $ctx->getvar($val);
3233 4 100       22 if (!defined $v) {
    50          
3234 2 50 33     11 unless (&parsing_func($ctx) || $ctx->{tainted}) {
3235 2 50       6 unless ($ctx->{skipundef}) {
3236 2 50       8 if ($ctx->is_superglobal($val)) {
3237             # superglobals always exist as empty array,
3238             # Since it is unknown which fields exist in a real environment,
3239             # don't create superglobal array on the fly here.
3240             #
3241 2         4 $res = 0;
3242             } else {
3243 0         0 $res = 1;
3244             }
3245 2         7 $to_num = 1;
3246             }
3247             }
3248             } elsif ($v eq '#unresolved') {
3249 2         13 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val is #unresolved");
3250             }
3251             } elsif ($val =~ /^(\#elem\d+)$/) {
3252 3         6 my ($v, $i) = @{$parser->{strmap}->{$val}};
  3         12  
3253 3         13 my ($basevar, $has_index, $idxstr) = $ctx->resolve_variable($val, 0);
3254 3 50       15 if (defined $basevar) {
3255 3         14 my $basestr = $ctx->exec_statement($basevar, 0);
3256 3 100 66     37 if (defined $basestr && is_array($basestr) && defined $idxstr) {
    50 66        
      0        
      33        
3257 2 50       8 $idxstr = $parser->setstr('') if is_null($idxstr); # null maps to '' array index
3258 2         6 my $arr = $parser->{strmap}{$basestr};
3259 2         8 my $arrval = $arr->get($idxstr);
3260 2 50       11 if (defined $arrval) {
    100          
3261 0         0 $res = 0;
3262             } elsif (!$ctx->is_superglobal($basevar)) {
3263 1         3 $res = 1;
3264             }
3265 2         5 $to_num = 1;
3266             } elsif (!defined $basestr && (!$ctx->is_superglobal($basevar) || ($basevar =~ /^\$GLOBALS$/))) {
3267 0 0       0 unless ($ctx->{tainted}) {
3268 0 0       0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val %s[%s] is #unresolved", $basevar, defined $i ? $i : '');
3269             }
3270             }
3271             }
3272             }
3273             }
3274             } elsif ($cmd eq 'is_null_weak') {
3275             # https://php.net/manual/en/types.comparisons.php (similar to $a == null)
3276             #
3277 1 50       6 if (scalar @$args >= 1) {
3278 1         5 my $val = $$args[0];
3279 1 50       4 if (is_array($val)) {
    50          
    50          
    50          
    0          
3280 0         0 $res = 1;
3281 0         0 $to_num = 1;
3282             } elsif (is_null($val)) {
3283 0         0 $res = 1;
3284 0         0 $to_num = 1;
3285             } elsif (is_strval($val)) {
3286 0         0 my $s = $parser->get_strval($val);
3287 0 0 0     0 if (!defined $s) {
    0 0        
    0          
    0          
    0          
3288 0         0 $res = 1;
3289             } elsif (($val =~ /^(\#num\d+)$/) && ($s == 0)) {
3290 0         0 $res = 1;
3291             } elsif ($s eq '0') {
3292 0         0 $res = 0;
3293             } elsif (($val =~ /^(\#str\d+)$/) && ($s eq '')) {
3294 0         0 $res = 1;
3295             } elsif ($val =~ /^(\#null)$/) {
3296 0         0 $res = 1;
3297             } else {
3298 0         0 $res = 0;
3299             }
3300 0         0 $to_num = 1;
3301             } elsif (is_variable($val)) {
3302 1         4 my $v = $ctx->getvar($val);
3303 1 50       7 if (!defined $v) {
    0          
3304 1 50 33     8 unless (&parsing_func($ctx) || $ctx->{tainted}) {
3305 0 0       0 unless ($ctx->{skipundef}) {
3306 0 0       0 if ($ctx->is_superglobal($val)) {
3307             # superglobals always exist as empty array,
3308             # Since it is unknown which fields exist in a real environment,
3309             # don't create superglobal array on the fly here.
3310             #
3311 0         0 $res = 0;
3312             } else {
3313 0         0 $res = 1;
3314             }
3315 0         0 $to_num = 1;
3316             }
3317             }
3318             } elsif ($v eq '#unresolved') {
3319 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val is #unresolved");
3320             }
3321             } elsif ($val =~ /^(\#elem\d+)$/) {
3322 0         0 my ($v, $i) = @{$parser->{strmap}->{$val}};
  0         0  
3323 0         0 my ($basevar, $has_index, $idxstr) = $ctx->resolve_variable($val, 0);
3324 0 0       0 if (defined $basevar) {
3325 0         0 my $basestr = $ctx->exec_statement($basevar, 0);
3326 0 0 0     0 if (defined $basestr && is_array($basestr) && defined $idxstr) {
    0 0        
      0        
      0        
3327 0 0       0 $idxstr = $parser->setstr('') if is_null($idxstr); # null maps to '' array index
3328 0         0 my $arr = $parser->{strmap}{$basestr};
3329 0         0 my $arrval = $arr->get($idxstr);
3330 0 0       0 if (defined $arrval) {
    0          
3331 0         0 my $s = $parser->get_strval($arrval);
3332 0 0 0     0 if (!defined $s) {
    0 0        
    0          
    0          
    0          
3333 0         0 $res = 1;
3334             } elsif (($arrval =~ /^(\#num\d+)$/) && ($s == 0)) {
3335 0         0 $res = 1;
3336             } elsif ($arrval eq '0') {
3337 0         0 $res = 1;
3338             } elsif (($arrval =~ /^(\#str\d+)$/) && ($s eq '')) {
3339 0         0 $res = 1;
3340             } elsif ($arrval =~ /^(\#null)$/) {
3341 0         0 $res = 1;
3342             } else {
3343 0         0 $res = 0;
3344             }
3345             } elsif (!$ctx->is_superglobal($basevar)) {
3346 0         0 $res = 1;
3347             }
3348 0         0 $to_num = 1;
3349             } elsif (!defined $basestr && (!$ctx->is_superglobal($basevar) || ($basevar =~ /^\$GLOBALS$/))) {
3350 0 0       0 unless ($ctx->{tainted}) {
3351 0 0       0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$val %s[%s] is #unresolved", $basevar, defined $i ? $i : '');
3352             }
3353             }
3354             }
3355             }
3356             }
3357             } elsif ($cmd eq 'isset') {
3358 11         508 my $inv = &exec_cmd($ctx, 'is_null', $args);
3359 11 100 66     60 if (defined $inv && is_strval($inv)) {
3360 6 100       20 if ($parser->{strmap}{$inv} eq '0') {
3361 4         14 $res = 1;
3362             } else {
3363 2         5 $res = 0;
3364             }
3365 6         11 $to_num = 1;
3366             }
3367             } elsif ($cmd eq 'define') {
3368 3 50       11 if (scalar @$args == 2) {
3369 3         12 my $key = $parser->get_strval($$args[0]);
3370 3         17 my $val = $parser->get_strval($$args[1]);
3371 3 50 33     15 if (defined $key && defined $val) {
3372 3 50       10 if (defined $ctx->{defines}) {
3373 3         7 $ctx->{defines}{$key} = $$args[1];
3374 3 50       13 $ctx->{log}->($ctx, 'cmd', $cmd, "$key = $val") if $ctx->{log};
3375             # no result here
3376 3         10 return '#noreturn';
3377             }
3378             }
3379             }
3380             } elsif ($cmd eq 'function_exists') {
3381             # https://php.net/manual/en/function.function-exists.php
3382             #
3383 10 50       36 if (scalar @$args == 1) {
3384 10         40 my $fn = $parser->get_strval($$args[0]);
3385 10 50       36 if (defined $fn) {
3386 10         23 my $lfn = lc($fn);
3387              
3388             # php functions are always global
3389             # - so a user defined func is valid also in tainted mode
3390             #
3391 10 100       42 unless ($ctx->{skipundef}) {
3392 9 100       29 if ($ctx->getfun($fn)) {
3393 2         7 $res = 1;
3394             }
3395             }
3396 10 100       28 unless (defined $res) {
3397 8 100       21 if (get_php_func($lfn)) {
3398 2         7 $res = 1;
3399             }
3400             }
3401 10 100       29 unless (defined $res) {
3402 6 50       16 if ($ctx->{tainted}) {
3403 0         0 $ctx->{warn}->($ctx, 'cmd', $cmd, "$fn not found but ctx tainted -> skip execution");
3404 0         0 return;
3405             } else {
3406 6         10 $res = 0;
3407             }
3408             }
3409 10         23 $to_num = 1;
3410             }
3411             }
3412             } elsif ($cmd eq 'create_function') {
3413             # https://php.net/manual/en/function.create-function.php
3414             # (see also: anonymous functions)
3415             #
3416             # This is basically what PHP does internally:
3417             # function create_function($args, $code) {
3418             # // create a random $functionName
3419             # eval('function ' . $functionName . '($args){$code}');
3420             # return $functionName;
3421             # }
3422             #
3423             # It's why you can do something like this (which works on the same principle as SQL injection):
3424             # $code = 'print "I print repeatedly.\n"; } print "I print once.\n"; if (false) {';
3425             # $function = create_function('', $code);
3426             # call_user_func($function);
3427             # call_user_func($function);
3428             #
3429             # // I print once.
3430             # // I print repeatedly.
3431             # // I print repeatedly.
3432             #
3433 12 50       38 if (scalar @$args == 2) {
3434 12         43 my $params = $parser->get_strval($$args[0]);
3435 12         33 my $code = $parser->get_strval($$args[1]);
3436 12 100 66     47 if (defined $args && defined $code) {
3437 9         17 my @arglist;
3438 9         27 foreach my $p (split(/,/, $params)) {
3439 4         36 my $stmt = $parser->read_statement([$p], undef);
3440 4         13 push(@arglist, $stmt);
3441             }
3442             # eval with new parser and without global & local varmap
3443             #
3444 9         40 my $codestr = $parser->setstr('function ('.$params.') {'.$code.'}');
3445 9         29 my $parser2 = $parser->subparser();
3446 9         39 my $ctx2 = $ctx->subctx(globals => {}, varmap => {}, parser => $parser2, tainted => 1, varhist => {});
3447 9         33 my $block = $ctx2->parse_eval($codestr);
3448 9         45 my $stmt = $ctx2->exec_eval($block);
3449 9         26 $stmt = $ctx->exec_statement($stmt);
3450 9         63 return $stmt;
3451             }
3452             }
3453             } elsif ($cmd eq 'call_user_func') {
3454             # https://php.net/manual/en/function.call-user-func.php
3455             #
3456 3 50       13 if (scalar @$args >= 1) {
3457 3         8 my $name = $$args[0];
3458 3 50 33     13 if (is_strval($name) || is_variable($name)) {
3459 3 50       11 if (is_strval($name)) {
3460 3         10 $name = $parser->{strmap}->{$name};
3461             }
3462 3         15 my @arglist = @$args[1..@$args-1];
3463 3         17 my $call = $parser->setcall($name, \@arglist);
3464 3         12 return $call;
3465             }
3466             }
3467             } elsif ($cmd eq 'call_user_func_array') {
3468             # https://php.net/manual/en/function.call-user-func-array.php
3469             #
3470 1 50       13 if (scalar @$args == 2) {
3471 1         3 my $name = $$args[0];
3472 1         3 my $param = $$args[1];
3473 1 50 33     15 if ((is_strval($name) || is_variable($name)) && is_array($param)) {
      33        
3474 1         3 my $arr = $parser->{strmap}{$param};
3475 1         5 my $keys = $arr->get_keys();
3476 1 50       4 if (is_strval($name)) {
3477 1         3 $name = $parser->{strmap}->{$name};
3478             }
3479 1 50       5 if (@$keys) {
3480 1         2 my @arglist = ();
3481 1         4 foreach my $k (@$keys) {
3482 1         3 my $val = $arr->val($k);
3483 1         5 my $v = $ctx->exec_statement($val);
3484 1         4 push(@arglist, $v);
3485             }
3486 1         6 my $call = $parser->setcall($name, \@arglist);
3487 1         5 return $call;
3488             }
3489             }
3490             }
3491             } elsif ($cmd eq 'file') {
3492             # https://php.net/manual/en/function.file.php
3493             # - TODO: also URL possible
3494             #
3495 0 0       0 if (scalar @$args >= 1) {
3496 0         0 my $filename = $parser->get_strval($$args[0]);
3497 0         0 my $flags = 0;
3498              
3499 0 0       0 if (scalar @$args > 1) {
3500 0         0 $flags = $parser->get_strval($$args[1]);
3501             }
3502 0 0       0 if (defined $filename) {
3503 0         0 my ($fh) = $parser->newfh($filename, 'r');
3504 0 0       0 if (defined $fh) {
3505 0         0 my $buf = $parser->{strmap}->{$fh}{buf};
3506 0         0 $parser->{strmap}->{$fh}{buf} = '';
3507             # split in lines and keep trailing newlines
3508             #
3509 0         0 my @lines;
3510 0 0       0 if ($flags =~ /FILE_IGNORE_NEW_LINES/) {
3511 0         0 @lines = split(/\n/, $buf);
3512             } else {
3513 0         0 @lines = split(/^/, $buf);
3514             }
3515 0         0 my $arr = $parser->newarr();
3516              
3517 0         0 foreach my $p (@lines) {
3518 0 0       0 $ctx->{log}->($ctx, 'cmd', $cmd, "[$filename] line: $p") if $ctx->{log};
3519 0         0 my $k = $parser->setstr($p);
3520 0         0 $arr->set(undef, $k);
3521             }
3522 0         0 return $arr->{name};
3523             }
3524             }
3525             }
3526             } elsif ($cmd eq 'file_get_contents') {
3527             # https://php.net/manual/en/function.file-get-contents.php
3528             #
3529 1 50       5 if (scalar @$args == 1) {
3530 1         6 my $filename = $parser->get_strval($$args[0]);
3531              
3532 1 50       5 if (defined $filename) {
3533 1         5 my ($fh) = $parser->newfh($filename, 'r');
3534 1 50       4 if (defined $fh) {
3535 0         0 my $buf = $parser->{strmap}->{$fh}{buf};
3536 0         0 $parser->{strmap}->{$fh}{buf} = '';
3537 0         0 $res = $buf;
3538             }
3539             }
3540             }
3541             } elsif ($cmd eq 'reset') {
3542             # https://php.net/manual/en/function.reset.php
3543             # reset next()-pointer & return first array element
3544             #
3545 0 0       0 if (scalar @$args == 1) {
3546 0         0 my $basestr = $$args[0];
3547              
3548 0 0       0 if (is_array($basestr)) {
3549 0         0 my $arr = $parser->{strmap}{$basestr};
3550 0         0 my $keys = $arr->get_keys();
3551              
3552             #my @vals = map { $arr->val($_) } @$keys;
3553 0         0 $res = $arr->val($keys->[0]);
3554             }
3555             }
3556             } elsif ($cmd eq 'fopen') {
3557 3 100       12 if (scalar @$args >= 2) {
3558 2         11 my $filename = $parser->get_strval($$args[0]);
3559 2         7 my $mode = $parser->get_strval($$args[1]);
3560 2 100 66     12 if (defined $filename && defined $mode) {
3561 1         7 my ($fh) = $parser->newfh($filename, $mode);
3562 1         5 return $fh;
3563             }
3564             }
3565             } elsif ($cmd eq 'fclose') {
3566 2 50       9 if (scalar @$args == 1) {
3567 2         6 my $fh = $$args[0];
3568 2 50       7 if ($fh =~ /^(\#fh\d+)$/) {
3569 0         0 $parser->{strmap}->{$fh}{pos} = 0;
3570 0         0 $parser->{strmap}->{$fh}{buf} = '';
3571             }
3572             }
3573             } elsif ($cmd eq 'fread') {
3574 0 0       0 if (scalar @$args == 2) {
3575 0         0 my $fh = $$args[0];
3576 0         0 my $cnt = $parser->get_strval($$args[1]);
3577 0 0 0     0 if (($fh =~ /^(\#fh\d+)$/) && defined $cnt) {
3578 0         0 my $pos = $parser->{strmap}->{$fh}{pos};
3579 0         0 my $buf = $parser->{strmap}->{$fh}{buf};
3580 0 0       0 if ($cnt > length($buf) - $pos) {
3581 0         0 $cnt = length($buf) - $pos;
3582             }
3583 0         0 $res = substr($buf, $pos, $cnt);
3584 0         0 $pos += $cnt;
3585 0         0 $parser->{strmap}->{$fh}{pos} = $pos;
3586             }
3587             }
3588             } elsif ($cmd eq 'fgets') {
3589 0 0       0 if (scalar @$args >= 1) {
3590 0         0 my $fh = $$args[0];
3591 0         0 my $cnt;
3592 0 0       0 if (scalar @$args == 2) {
3593 0         0 $cnt = $parser->get_strval($$args[1]);
3594             }
3595 0 0       0 if ($fh =~ /^(\#fh\d+)$/) {
3596 0         0 my $pos = $parser->{strmap}->{$fh}{pos};
3597 0         0 my $buf = $parser->{strmap}->{$fh}{buf};
3598 0         0 $res = '';
3599              
3600 0         0 while ($pos < length($buf)) {
3601 0         0 my $ch = substr($buf, $pos, 1);
3602 0 0       0 if ($ch eq "\n") {
3603 0         0 $pos++;
3604 0         0 last;
3605             }
3606 0 0 0     0 last if (defined $cnt && (length($res) >= $cnt));
3607 0         0 $res .= $ch;
3608 0         0 $pos++;
3609             }
3610 0         0 $parser->{strmap}->{$fh}{pos} = $pos;
3611             }
3612             }
3613             }
3614              
3615 401 100       1249 if (defined $res) {
3616 119         176 my $k;
3617 119 100       257 if ($to_num) {
3618 79         229 $k = $parser->setnum($res);
3619             } else {
3620 40         141 $k = $parser->setstr($res);
3621             }
3622 119         406 return $k;
3623             }
3624 282         817 return;
3625             }
3626              
3627             1;
3628              
3629             __END__