1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Andi Gutmans <andi@php.net> |
14 | Zeev Suraski <zeev@php.net> |
15 | Rasmus Lerdorf <rasmus@php.net> |
16 | Andrei Zmievski <andrei@php.net> |
17 | Stig Venaas <venaas@php.net> |
18 | Jason Greene <jason@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include "php.h"
23 #include "php_ini.h"
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef PHP_WIN32
31 #include "win32/unistd.h"
32 #endif
33 #include "zend_globals.h"
34 #include "zend_interfaces.h"
35 #include "php_globals.h"
36 #include "php_array.h"
37 #include "basic_functions.h"
38 #include "php_string.h"
39 #include "php_math.h"
40 #include "zend_smart_str.h"
41 #include "zend_bitset.h"
42 #include "zend_exceptions.h"
43 #include "ext/spl/spl_array.h"
44 #include "ext/random/php_random.h"
45
46 /* {{{ defines */
47
48 #define DIFF_NORMAL 1
49 #define DIFF_KEY 2
50 #define DIFF_ASSOC 6
51 #define DIFF_COMP_DATA_NONE -1
52 #define DIFF_COMP_DATA_INTERNAL 0
53 #define DIFF_COMP_DATA_USER 1
54 #define DIFF_COMP_KEY_INTERNAL 0
55 #define DIFF_COMP_KEY_USER 1
56
57 #define INTERSECT_NORMAL 1
58 #define INTERSECT_KEY 2
59 #define INTERSECT_ASSOC 6
60 #define INTERSECT_COMP_DATA_NONE -1
61 #define INTERSECT_COMP_DATA_INTERNAL 0
62 #define INTERSECT_COMP_DATA_USER 1
63 #define INTERSECT_COMP_KEY_INTERNAL 0
64 #define INTERSECT_COMP_KEY_USER 1
65 /* }}} */
66
ZEND_DECLARE_MODULE_GLOBALS(array)67 ZEND_DECLARE_MODULE_GLOBALS(array)
68
69 /* {{{ php_array_init_globals */
70 static void php_array_init_globals(zend_array_globals *array_globals)
71 {
72 memset(array_globals, 0, sizeof(zend_array_globals));
73 }
74 /* }}} */
75
PHP_MINIT_FUNCTION(array)76 PHP_MINIT_FUNCTION(array) /* {{{ */
77 {
78 ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
79
80 return SUCCESS;
81 }
82 /* }}} */
83
PHP_MSHUTDOWN_FUNCTION(array)84 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
85 {
86 #ifdef ZTS
87 ts_free_id(array_globals_id);
88 #endif
89
90 return SUCCESS;
91 }
92 /* }}} */
93
stable_sort_fallback(Bucket * a,Bucket * b)94 static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
95 if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
96 return 1;
97 } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
98 return -1;
99 } else {
100 return 0;
101 }
102 }
103
104 #define RETURN_STABLE_SORT(a, b, result) do { \
105 int _result = (result); \
106 if (EXPECTED(_result)) { \
107 return _result; \
108 } \
109 return stable_sort_fallback((a), (b)); \
110 } while (0)
111
112 /* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
113 #define DEFINE_SORT_VARIANTS(name) \
114 static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
115 return php_array_##name##_unstable_i(a, b); \
116 } \
117 static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
118 RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
119 } \
120 static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
121 return php_array_##name##_unstable(a, b) * -1; \
122 } \
123 static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
124 RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
125 } \
126
php_array_key_compare_unstable_i(Bucket * f,Bucket * s)127 static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
128 {
129 zval first;
130 zval second;
131
132 if (f->key == NULL && s->key == NULL) {
133 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
134 } else if (f->key && s->key) {
135 return zendi_smart_strcmp(f->key, s->key);
136 }
137 if (f->key) {
138 ZVAL_STR(&first, f->key);
139 } else {
140 ZVAL_LONG(&first, f->h);
141 }
142 if (s->key) {
143 ZVAL_STR(&second, s->key);
144 } else {
145 ZVAL_LONG(&second, s->h);
146 }
147 return zend_compare(&first, &second);
148 }
149 /* }}} */
150
php_array_key_compare_numeric_unstable_i(Bucket * f,Bucket * s)151 static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
152 {
153 if (f->key == NULL && s->key == NULL) {
154 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
155 } else {
156 double d1, d2;
157 if (f->key) {
158 d1 = zend_strtod(f->key->val, NULL);
159 } else {
160 d1 = (double)(zend_long)f->h;
161 }
162 if (s->key) {
163 d2 = zend_strtod(s->key->val, NULL);
164 } else {
165 d2 = (double)(zend_long)s->h;
166 }
167 return ZEND_THREEWAY_COMPARE(d1, d2);
168 }
169 }
170 /* }}} */
171
php_array_key_compare_string_case_unstable_i(Bucket * f,Bucket * s)172 static zend_always_inline int php_array_key_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
173 {
174 const char *s1, *s2;
175 size_t l1, l2;
176 char buf1[MAX_LENGTH_OF_LONG + 1];
177 char buf2[MAX_LENGTH_OF_LONG + 1];
178
179 if (f->key) {
180 s1 = f->key->val;
181 l1 = f->key->len;
182 } else {
183 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
184 l1 = buf1 + sizeof(buf1) - 1 - s1;
185 }
186 if (s->key) {
187 s2 = s->key->val;
188 l2 = s->key->len;
189 } else {
190 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
191 l2 = buf2 + sizeof(buf2) - 1 - s1;
192 }
193 return zend_binary_strcasecmp_l(s1, l1, s2, l2);
194 }
195 /* }}} */
196
php_array_key_compare_string_unstable_i(Bucket * f,Bucket * s)197 static zend_always_inline int php_array_key_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
198 {
199 const char *s1, *s2;
200 size_t l1, l2;
201 char buf1[MAX_LENGTH_OF_LONG + 1];
202 char buf2[MAX_LENGTH_OF_LONG + 1];
203
204 if (f->key) {
205 s1 = f->key->val;
206 l1 = f->key->len;
207 } else {
208 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
209 l1 = buf1 + sizeof(buf1) - 1 - s1;
210 }
211 if (s->key) {
212 s2 = s->key->val;
213 l2 = s->key->len;
214 } else {
215 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
216 l2 = buf2 + sizeof(buf2) - 1 - s2;
217 }
218 return zend_binary_strcmp(s1, l1, s2, l2);
219 }
220 /* }}} */
221
php_array_key_compare_string_natural_general(Bucket * f,Bucket * s,int fold_case)222 static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case) /* {{{ */
223 {
224 const char *s1, *s2;
225 size_t l1, l2;
226 char buf1[MAX_LENGTH_OF_LONG + 1];
227 char buf2[MAX_LENGTH_OF_LONG + 1];
228
229 if (f->key) {
230 s1 = f->key->val;
231 l1 = f->key->len;
232 } else {
233 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
234 l1 = buf1 + sizeof(buf1) - 1 - s1;
235 }
236 if (s->key) {
237 s2 = s->key->val;
238 l2 = s->key->len;
239 } else {
240 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
241 l2 = buf2 + sizeof(buf2) - 1 - s1;
242 }
243 return strnatcmp_ex(s1, l1, s2, l2, fold_case);
244 }
245 /* }}} */
246
php_array_key_compare_string_natural_case(Bucket * a,Bucket * b)247 static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
248 {
249 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 1));
250 }
251 /* }}} */
252
php_array_reverse_key_compare_string_natural_case(Bucket * a,Bucket * b)253 static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
254 {
255 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 1));
256 }
257 /* }}} */
258
php_array_key_compare_string_natural(Bucket * a,Bucket * b)259 static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
260 {
261 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 0));
262 }
263 /* }}} */
264
php_array_reverse_key_compare_string_natural(Bucket * a,Bucket * b)265 static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
266 {
267 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 0));
268 }
269 /* }}} */
270
php_array_key_compare_string_locale_unstable_i(Bucket * f,Bucket * s)271 static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
272 {
273 const char *s1, *s2;
274 char buf1[MAX_LENGTH_OF_LONG + 1];
275 char buf2[MAX_LENGTH_OF_LONG + 1];
276
277 if (f->key) {
278 s1 = f->key->val;
279 } else {
280 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
281 }
282 if (s->key) {
283 s2 = s->key->val;
284 } else {
285 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
286 }
287 return strcoll(s1, s2);
288 }
289 /* }}} */
290
php_array_data_compare_unstable_i(Bucket * f,Bucket * s)291 static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
292 {
293 int result = zend_compare(&f->val, &s->val);
294 /* Special enums handling for array_unique. We don't want to add this logic to zend_compare as
295 * that would be observable via comparison operators. */
296 zval *rhs = &s->val;
297 ZVAL_DEREF(rhs);
298 if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
299 && result == ZEND_UNCOMPARABLE
300 && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
301 zval *lhs = &f->val;
302 ZVAL_DEREF(lhs);
303 if (Z_TYPE_P(lhs) == IS_OBJECT && (Z_OBJCE_P(lhs)->ce_flags & ZEND_ACC_ENUM)) {
304 // Order doesn't matter, we just need to group the same enum values
305 uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
306 uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
307 return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
308 } else {
309 // Shift enums to the end of the array
310 return -1;
311 }
312 }
313 return result;
314 }
315 /* }}} */
316
php_array_data_compare_numeric_unstable_i(Bucket * f,Bucket * s)317 static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
318 {
319 return numeric_compare_function(&f->val, &s->val);
320 }
321 /* }}} */
322
php_array_data_compare_string_case_unstable_i(Bucket * f,Bucket * s)323 static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
324 {
325 return string_case_compare_function(&f->val, &s->val);
326 }
327 /* }}} */
328
php_array_data_compare_string_unstable_i(Bucket * f,Bucket * s)329 static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
330 {
331 return string_compare_function(&f->val, &s->val);
332 }
333 /* }}} */
334
php_array_natural_general_compare(Bucket * f,Bucket * s,int fold_case)335 static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */
336 {
337 zend_string *tmp_str1, *tmp_str2;
338 zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
339 zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
340
341 int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
342
343 zend_tmp_string_release(tmp_str1);
344 zend_tmp_string_release(tmp_str2);
345 return result;
346 }
347 /* }}} */
348
php_array_natural_compare_unstable_i(Bucket * a,Bucket * b)349 static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
350 {
351 return php_array_natural_general_compare(a, b, 0);
352 }
353 /* }}} */
354
php_array_natural_case_compare_unstable_i(Bucket * a,Bucket * b)355 static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
356 {
357 return php_array_natural_general_compare(a, b, 1);
358 }
359 /* }}} */
360
php_array_data_compare_string_locale_unstable_i(Bucket * f,Bucket * s)361 static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
362 {
363 return string_locale_compare_function(&f->val, &s->val);
364 }
365 /* }}} */
366
367 DEFINE_SORT_VARIANTS(key_compare);
368 DEFINE_SORT_VARIANTS(key_compare_numeric);
369 DEFINE_SORT_VARIANTS(key_compare_string_case);
370 DEFINE_SORT_VARIANTS(key_compare_string);
371 DEFINE_SORT_VARIANTS(key_compare_string_locale);
372 DEFINE_SORT_VARIANTS(data_compare);
373 DEFINE_SORT_VARIANTS(data_compare_numeric);
374 DEFINE_SORT_VARIANTS(data_compare_string_case);
375 DEFINE_SORT_VARIANTS(data_compare_string);
376 DEFINE_SORT_VARIANTS(data_compare_string_locale);
377 DEFINE_SORT_VARIANTS(natural_compare);
378 DEFINE_SORT_VARIANTS(natural_case_compare);
379
php_get_key_compare_func(zend_long sort_type,int reverse)380 static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */
381 {
382 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
383 case PHP_SORT_NUMERIC:
384 if (reverse) {
385 return php_array_reverse_key_compare_numeric;
386 } else {
387 return php_array_key_compare_numeric;
388 }
389 break;
390
391 case PHP_SORT_STRING:
392 if (sort_type & PHP_SORT_FLAG_CASE) {
393 if (reverse) {
394 return php_array_reverse_key_compare_string_case;
395 } else {
396 return php_array_key_compare_string_case;
397 }
398 } else {
399 if (reverse) {
400 return php_array_reverse_key_compare_string;
401 } else {
402 return php_array_key_compare_string;
403 }
404 }
405 break;
406
407 case PHP_SORT_NATURAL:
408 if (sort_type & PHP_SORT_FLAG_CASE) {
409 if (reverse) {
410 return php_array_reverse_key_compare_string_natural_case;
411 } else {
412 return php_array_key_compare_string_natural_case;
413 }
414 } else {
415 if (reverse) {
416 return php_array_reverse_key_compare_string_natural;
417 } else {
418 return php_array_key_compare_string_natural;
419 }
420 }
421 break;
422
423 case PHP_SORT_LOCALE_STRING:
424 if (reverse) {
425 return php_array_reverse_key_compare_string_locale;
426 } else {
427 return php_array_key_compare_string_locale;
428 }
429 break;
430
431 case PHP_SORT_REGULAR:
432 default:
433 if (reverse) {
434 return php_array_reverse_key_compare;
435 } else {
436 return php_array_key_compare;
437 }
438 break;
439 }
440 return NULL;
441 }
442 /* }}} */
443
php_get_data_compare_func(zend_long sort_type,int reverse)444 static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */
445 {
446 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
447 case PHP_SORT_NUMERIC:
448 if (reverse) {
449 return php_array_reverse_data_compare_numeric;
450 } else {
451 return php_array_data_compare_numeric;
452 }
453 break;
454
455 case PHP_SORT_STRING:
456 if (sort_type & PHP_SORT_FLAG_CASE) {
457 if (reverse) {
458 return php_array_reverse_data_compare_string_case;
459 } else {
460 return php_array_data_compare_string_case;
461 }
462 } else {
463 if (reverse) {
464 return php_array_reverse_data_compare_string;
465 } else {
466 return php_array_data_compare_string;
467 }
468 }
469 break;
470
471 case PHP_SORT_NATURAL:
472 if (sort_type & PHP_SORT_FLAG_CASE) {
473 if (reverse) {
474 return php_array_reverse_natural_case_compare;
475 } else {
476 return php_array_natural_case_compare;
477 }
478 } else {
479 if (reverse) {
480 return php_array_reverse_natural_compare;
481 } else {
482 return php_array_natural_compare;
483 }
484 }
485 break;
486
487 case PHP_SORT_LOCALE_STRING:
488 if (reverse) {
489 return php_array_reverse_data_compare_string_locale;
490 } else {
491 return php_array_data_compare_string_locale;
492 }
493 break;
494
495 case PHP_SORT_REGULAR:
496 default:
497 if (reverse) {
498 return php_array_reverse_data_compare;
499 } else {
500 return php_array_data_compare;
501 }
502 break;
503 }
504 return NULL;
505 }
506 /* }}} */
507
php_get_data_compare_func_unstable(zend_long sort_type,int reverse)508 static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, int reverse) /* {{{ */
509 {
510 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
511 case PHP_SORT_NUMERIC:
512 if (reverse) {
513 return php_array_reverse_data_compare_numeric_unstable;
514 } else {
515 return php_array_data_compare_numeric_unstable;
516 }
517 break;
518
519 case PHP_SORT_STRING:
520 if (sort_type & PHP_SORT_FLAG_CASE) {
521 if (reverse) {
522 return php_array_reverse_data_compare_string_case_unstable;
523 } else {
524 return php_array_data_compare_string_case_unstable;
525 }
526 } else {
527 if (reverse) {
528 return php_array_reverse_data_compare_string_unstable;
529 } else {
530 return php_array_data_compare_string_unstable;
531 }
532 }
533 break;
534
535 case PHP_SORT_NATURAL:
536 if (sort_type & PHP_SORT_FLAG_CASE) {
537 if (reverse) {
538 return php_array_reverse_natural_case_compare_unstable;
539 } else {
540 return php_array_natural_case_compare_unstable;
541 }
542 } else {
543 if (reverse) {
544 return php_array_reverse_natural_compare_unstable;
545 } else {
546 return php_array_natural_compare_unstable;
547 }
548 }
549 break;
550
551 case PHP_SORT_LOCALE_STRING:
552 if (reverse) {
553 return php_array_reverse_data_compare_string_locale_unstable;
554 } else {
555 return php_array_data_compare_string_locale_unstable;
556 }
557 break;
558
559 case PHP_SORT_REGULAR:
560 default:
561 if (reverse) {
562 return php_array_reverse_data_compare_unstable;
563 } else {
564 return php_array_data_compare_unstable;
565 }
566 break;
567 }
568 return NULL;
569 }
570 /* }}} */
571
572 /* {{{ Sort an array by key value in reverse order */
PHP_FUNCTION(krsort)573 PHP_FUNCTION(krsort)
574 {
575 zval *array;
576 zend_long sort_type = PHP_SORT_REGULAR;
577 bucket_compare_func_t cmp;
578
579 ZEND_PARSE_PARAMETERS_START(1, 2)
580 Z_PARAM_ARRAY_EX(array, 0, 1)
581 Z_PARAM_OPTIONAL
582 Z_PARAM_LONG(sort_type)
583 ZEND_PARSE_PARAMETERS_END();
584
585 cmp = php_get_key_compare_func(sort_type, 1);
586
587 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
588
589 RETURN_TRUE;
590 }
591 /* }}} */
592
593 /* {{{ Sort an array by key */
PHP_FUNCTION(ksort)594 PHP_FUNCTION(ksort)
595 {
596 zval *array;
597 zend_long sort_type = PHP_SORT_REGULAR;
598 bucket_compare_func_t cmp;
599
600 ZEND_PARSE_PARAMETERS_START(1, 2)
601 Z_PARAM_ARRAY_EX(array, 0, 1)
602 Z_PARAM_OPTIONAL
603 Z_PARAM_LONG(sort_type)
604 ZEND_PARSE_PARAMETERS_END();
605
606 cmp = php_get_key_compare_func(sort_type, 0);
607
608 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
609
610 RETURN_TRUE;
611 }
612 /* }}} */
613
php_count_recursive(HashTable * ht)614 PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
615 {
616 zend_long cnt = 0;
617 zval *element;
618
619 if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
620 if (GC_IS_RECURSIVE(ht)) {
621 php_error_docref(NULL, E_WARNING, "Recursion detected");
622 return 0;
623 }
624 GC_PROTECT_RECURSION(ht);
625 }
626
627 cnt = zend_hash_num_elements(ht);
628 ZEND_HASH_FOREACH_VAL(ht, element) {
629 ZVAL_DEREF(element);
630 if (Z_TYPE_P(element) == IS_ARRAY) {
631 cnt += php_count_recursive(Z_ARRVAL_P(element));
632 }
633 } ZEND_HASH_FOREACH_END();
634
635 GC_TRY_UNPROTECT_RECURSION(ht);
636 return cnt;
637 }
638 /* }}} */
639
640 /* {{{ Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)641 PHP_FUNCTION(count)
642 {
643 zval *array;
644 zend_long mode = PHP_COUNT_NORMAL;
645 zend_long cnt;
646
647 ZEND_PARSE_PARAMETERS_START(1, 2)
648 Z_PARAM_ZVAL(array)
649 Z_PARAM_OPTIONAL
650 Z_PARAM_LONG(mode)
651 ZEND_PARSE_PARAMETERS_END();
652
653 if (mode != PHP_COUNT_NORMAL && mode != PHP_COUNT_RECURSIVE) {
654 zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
655 RETURN_THROWS();
656 }
657
658 switch (Z_TYPE_P(array)) {
659 case IS_ARRAY:
660 if (mode != PHP_COUNT_RECURSIVE) {
661 cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
662 } else {
663 cnt = php_count_recursive(Z_ARRVAL_P(array));
664 }
665 RETURN_LONG(cnt);
666 break;
667 case IS_OBJECT: {
668 zval retval;
669 /* first, we check if the handler is defined */
670 zend_object *zobj = Z_OBJ_P(array);
671 if (zobj->handlers->count_elements) {
672 RETVAL_LONG(1);
673 if (SUCCESS == zobj->handlers->count_elements(zobj, &Z_LVAL_P(return_value))) {
674 return;
675 }
676 if (EG(exception)) {
677 RETURN_THROWS();
678 }
679 }
680 /* if not and the object implements Countable we call its count() method */
681 if (instanceof_function(zobj->ce, zend_ce_countable)) {
682 zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT));
683 zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval);
684 if (Z_TYPE(retval) != IS_UNDEF) {
685 RETVAL_LONG(zval_get_long(&retval));
686 zval_ptr_dtor(&retval);
687 }
688 return;
689 }
690 }
691 ZEND_FALLTHROUGH;
692 default:
693 zend_argument_type_error(1, "must be of type Countable|array, %s given", zend_zval_value_name(array));
694 RETURN_THROWS();
695 }
696 }
697 /* }}} */
698
php_natsort(INTERNAL_FUNCTION_PARAMETERS,int fold_case)699 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
700 {
701 zval *array;
702
703 ZEND_PARSE_PARAMETERS_START(1, 1)
704 Z_PARAM_ARRAY_EX(array, 0, 1)
705 ZEND_PARSE_PARAMETERS_END();
706
707 if (fold_case) {
708 zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
709 } else {
710 zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
711 }
712
713 RETURN_TRUE;
714 }
715 /* }}} */
716
717 /* {{{ Sort an array using natural sort */
PHP_FUNCTION(natsort)718 PHP_FUNCTION(natsort)
719 {
720 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
721 }
722 /* }}} */
723
724 /* {{{ Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)725 PHP_FUNCTION(natcasesort)
726 {
727 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
728 }
729 /* }}} */
730
731 /* {{{ Sort an array and maintain index association */
PHP_FUNCTION(asort)732 PHP_FUNCTION(asort)
733 {
734 zval *array;
735 zend_long sort_type = PHP_SORT_REGULAR;
736 bucket_compare_func_t cmp;
737
738 ZEND_PARSE_PARAMETERS_START(1, 2)
739 Z_PARAM_ARRAY_EX(array, 0, 1)
740 Z_PARAM_OPTIONAL
741 Z_PARAM_LONG(sort_type)
742 ZEND_PARSE_PARAMETERS_END();
743
744 cmp = php_get_data_compare_func(sort_type, 0);
745
746 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
747
748 RETURN_TRUE;
749 }
750 /* }}} */
751
752 /* {{{ Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)753 PHP_FUNCTION(arsort)
754 {
755 zval *array;
756 zend_long sort_type = PHP_SORT_REGULAR;
757 bucket_compare_func_t cmp;
758
759 ZEND_PARSE_PARAMETERS_START(1, 2)
760 Z_PARAM_ARRAY_EX(array, 0, 1)
761 Z_PARAM_OPTIONAL
762 Z_PARAM_LONG(sort_type)
763 ZEND_PARSE_PARAMETERS_END();
764
765 cmp = php_get_data_compare_func(sort_type, 1);
766
767 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
768
769 RETURN_TRUE;
770 }
771 /* }}} */
772
773 /* {{{ Sort an array */
PHP_FUNCTION(sort)774 PHP_FUNCTION(sort)
775 {
776 zval *array;
777 zend_long sort_type = PHP_SORT_REGULAR;
778 bucket_compare_func_t cmp;
779
780 ZEND_PARSE_PARAMETERS_START(1, 2)
781 Z_PARAM_ARRAY_EX(array, 0, 1)
782 Z_PARAM_OPTIONAL
783 Z_PARAM_LONG(sort_type)
784 ZEND_PARSE_PARAMETERS_END();
785
786 cmp = php_get_data_compare_func(sort_type, 0);
787
788 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
789
790 RETURN_TRUE;
791 }
792 /* }}} */
793
794 /* {{{ Sort an array in reverse order */
PHP_FUNCTION(rsort)795 PHP_FUNCTION(rsort)
796 {
797 zval *array;
798 zend_long sort_type = PHP_SORT_REGULAR;
799 bucket_compare_func_t cmp;
800
801 ZEND_PARSE_PARAMETERS_START(1, 2)
802 Z_PARAM_ARRAY_EX(array, 0, 1)
803 Z_PARAM_OPTIONAL
804 Z_PARAM_LONG(sort_type)
805 ZEND_PARSE_PARAMETERS_END();
806
807 cmp = php_get_data_compare_func(sort_type, 1);
808
809 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
810
811 RETURN_TRUE;
812 }
813 /* }}} */
814
php_array_user_compare_unstable(Bucket * f,Bucket * s)815 static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
816 {
817 zval args[2];
818 zval retval;
819 bool call_failed;
820
821 ZVAL_COPY(&args[0], &f->val);
822 ZVAL_COPY(&args[1], &s->val);
823
824 BG(user_compare_fci).param_count = 2;
825 BG(user_compare_fci).params = args;
826 BG(user_compare_fci).retval = &retval;
827 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
828 zval_ptr_dtor(&args[1]);
829 zval_ptr_dtor(&args[0]);
830 if (UNEXPECTED(call_failed)) {
831 return 0;
832 }
833
834 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
835 if (!ARRAYG(compare_deprecation_thrown)) {
836 php_error_docref(NULL, E_DEPRECATED,
837 "Returning bool from comparison function is deprecated, "
838 "return an integer less than, equal to, or greater than zero");
839 ARRAYG(compare_deprecation_thrown) = 1;
840 }
841
842 if (Z_TYPE(retval) == IS_FALSE) {
843 /* Retry with swapped operands. */
844 ZVAL_COPY(&args[0], &s->val);
845 ZVAL_COPY(&args[1], &f->val);
846 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
847 zval_ptr_dtor(&args[1]);
848 zval_ptr_dtor(&args[0]);
849 if (call_failed) {
850 return 0;
851 }
852
853 zend_long ret = zval_get_long(&retval);
854 zval_ptr_dtor(&retval);
855 return -ZEND_NORMALIZE_BOOL(ret);
856 }
857 }
858
859 zend_long ret = zval_get_long(&retval);
860 zval_ptr_dtor(&retval);
861 return ZEND_NORMALIZE_BOOL(ret);
862 }
863 /* }}} */
864
php_array_user_compare(Bucket * a,Bucket * b)865 static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
866 {
867 RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
868 }
869 /* }}} */
870
871 #define PHP_ARRAY_CMP_FUNC_VARS \
872 zend_fcall_info old_user_compare_fci; \
873 zend_fcall_info_cache old_user_compare_fci_cache \
874
875 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
876 old_user_compare_fci = BG(user_compare_fci); \
877 old_user_compare_fci_cache = BG(user_compare_fci_cache); \
878 ARRAYG(compare_deprecation_thrown) = 0; \
879 BG(user_compare_fci_cache) = empty_fcall_info_cache; \
880
881 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
882 BG(user_compare_fci) = old_user_compare_fci; \
883 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
884
php_usort(INTERNAL_FUNCTION_PARAMETERS,bucket_compare_func_t compare_func,bool renumber)885 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, bool renumber) /* {{{ */
886 {
887 zval *array;
888 zend_array *arr;
889 PHP_ARRAY_CMP_FUNC_VARS;
890
891 PHP_ARRAY_CMP_FUNC_BACKUP();
892
893 ZEND_PARSE_PARAMETERS_START(2, 2)
894 Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
895 Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
896 ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
897
898 arr = Z_ARR_P(array);
899 if (zend_hash_num_elements(arr) == 0) {
900 PHP_ARRAY_CMP_FUNC_RESTORE();
901 RETURN_TRUE;
902 }
903
904 /* Copy array, so the in-place modifications will not be visible to the callback function.
905 * Unless there are no other references since we know for sure it won't be visible. */
906 bool in_place = zend_may_modify_arg_in_place(array);
907 if (!in_place) {
908 arr = zend_array_dup(arr);
909 }
910
911 zend_hash_sort(arr, compare_func, renumber);
912
913 if (in_place) {
914 GC_ADDREF(arr);
915 }
916
917 zval garbage;
918 ZVAL_COPY_VALUE(&garbage, array);
919 ZVAL_ARR(array, arr);
920 zval_ptr_dtor(&garbage);
921
922 PHP_ARRAY_CMP_FUNC_RESTORE();
923 RETURN_TRUE;
924 }
925 /* }}} */
926
927 /* {{{ Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)928 PHP_FUNCTION(usort)
929 {
930 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
931 }
932 /* }}} */
933
934 /* {{{ Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)935 PHP_FUNCTION(uasort)
936 {
937 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
938 }
939 /* }}} */
940
php_array_user_key_compare_unstable(Bucket * f,Bucket * s)941 static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
942 {
943 zval args[2];
944 zval retval;
945 bool call_failed;
946
947 if (f->key == NULL) {
948 ZVAL_LONG(&args[0], f->h);
949 } else {
950 ZVAL_STR_COPY(&args[0], f->key);
951 }
952 if (s->key == NULL) {
953 ZVAL_LONG(&args[1], s->h);
954 } else {
955 ZVAL_STR_COPY(&args[1], s->key);
956 }
957
958 BG(user_compare_fci).param_count = 2;
959 BG(user_compare_fci).params = args;
960 BG(user_compare_fci).retval = &retval;
961 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
962 zval_ptr_dtor(&args[1]);
963 zval_ptr_dtor(&args[0]);
964 if (UNEXPECTED(call_failed)) {
965 return 0;
966 }
967
968 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
969 if (!ARRAYG(compare_deprecation_thrown)) {
970 php_error_docref(NULL, E_DEPRECATED,
971 "Returning bool from comparison function is deprecated, "
972 "return an integer less than, equal to, or greater than zero");
973 ARRAYG(compare_deprecation_thrown) = 1;
974 }
975
976 if (Z_TYPE(retval) == IS_FALSE) {
977 /* Retry with swapped operands. */
978 if (s->key == NULL) {
979 ZVAL_LONG(&args[0], s->h);
980 } else {
981 ZVAL_STR_COPY(&args[0], s->key);
982 }
983 if (f->key == NULL) {
984 ZVAL_LONG(&args[1], f->h);
985 } else {
986 ZVAL_STR_COPY(&args[1], f->key);
987 }
988
989 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
990 zval_ptr_dtor(&args[1]);
991 zval_ptr_dtor(&args[0]);
992 if (call_failed) {
993 return 0;
994 }
995
996 zend_long ret = zval_get_long(&retval);
997 zval_ptr_dtor(&retval);
998 return -ZEND_NORMALIZE_BOOL(ret);
999 }
1000 }
1001
1002 zend_long result = zval_get_long(&retval);
1003 zval_ptr_dtor(&retval);
1004 return ZEND_NORMALIZE_BOOL(result);
1005 }
1006 /* }}} */
1007
php_array_user_key_compare(Bucket * a,Bucket * b)1008 static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */
1009 {
1010 RETURN_STABLE_SORT(a, b, php_array_user_key_compare_unstable(a, b));
1011 }
1012 /* }}} */
1013
1014 /* {{{ Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)1015 PHP_FUNCTION(uksort)
1016 {
1017 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1018 }
1019 /* }}} */
1020
get_ht_for_iap(zval * zv,bool separate)1021 static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
1022 if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
1023 return Z_ARRVAL_P(zv);
1024 }
1025
1026 ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
1027 php_error_docref(NULL, E_DEPRECATED,
1028 "Calling %s() on an object is deprecated", get_active_function_name());
1029
1030 zend_object *zobj = Z_OBJ_P(zv);
1031 if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
1032 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
1033 GC_DELREF(zobj->properties);
1034 }
1035 zobj->properties = zend_array_dup(zobj->properties);
1036 }
1037 return zobj->handlers->get_properties(zobj);
1038 }
1039
1040 /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1041 PHP_FUNCTION(end)
1042 {
1043 zval *array_zv;
1044 zval *entry;
1045
1046 ZEND_PARSE_PARAMETERS_START(1, 1)
1047 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1048 ZEND_PARSE_PARAMETERS_END();
1049
1050 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1051 if (zend_hash_num_elements(array) == 0) {
1052 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1053 RETURN_FALSE;
1054 }
1055 zend_hash_internal_pointer_end(array);
1056
1057 if (USED_RET()) {
1058 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1059 RETURN_FALSE;
1060 }
1061
1062 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1063 entry = Z_INDIRECT_P(entry);
1064 }
1065
1066 RETURN_COPY_DEREF(entry);
1067 }
1068 }
1069 /* }}} */
1070
1071 /* {{{ Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1072 PHP_FUNCTION(prev)
1073 {
1074 zval *array_zv;
1075 zval *entry;
1076
1077 ZEND_PARSE_PARAMETERS_START(1, 1)
1078 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1079 ZEND_PARSE_PARAMETERS_END();
1080
1081 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1082 if (zend_hash_num_elements(array) == 0) {
1083 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1084 RETURN_FALSE;
1085 }
1086 zend_hash_move_backwards(array);
1087
1088 if (USED_RET()) {
1089 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1090 RETURN_FALSE;
1091 }
1092
1093 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1094 entry = Z_INDIRECT_P(entry);
1095 }
1096
1097 RETURN_COPY_DEREF(entry);
1098 }
1099 }
1100 /* }}} */
1101
1102 /* {{{ Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1103 PHP_FUNCTION(next)
1104 {
1105 zval *array_zv;
1106 zval *entry;
1107
1108 ZEND_PARSE_PARAMETERS_START(1, 1)
1109 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1110 ZEND_PARSE_PARAMETERS_END();
1111
1112 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1113 if (zend_hash_num_elements(array) == 0) {
1114 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1115 RETURN_FALSE;
1116 }
1117 zend_hash_move_forward(array);
1118
1119 if (USED_RET()) {
1120 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1121 RETURN_FALSE;
1122 }
1123
1124 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1125 entry = Z_INDIRECT_P(entry);
1126 }
1127
1128 RETURN_COPY_DEREF(entry);
1129 }
1130 }
1131 /* }}} */
1132
1133 /* {{{ Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1134 PHP_FUNCTION(reset)
1135 {
1136 zval *array_zv;
1137 zval *entry;
1138
1139 ZEND_PARSE_PARAMETERS_START(1, 1)
1140 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1141 ZEND_PARSE_PARAMETERS_END();
1142
1143 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1144 if (zend_hash_num_elements(array) == 0) {
1145 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1146 RETURN_FALSE;
1147 }
1148 zend_hash_internal_pointer_reset(array);
1149
1150 if (USED_RET()) {
1151 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1152 RETURN_FALSE;
1153 }
1154
1155 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1156 entry = Z_INDIRECT_P(entry);
1157 }
1158
1159 RETURN_COPY_DEREF(entry);
1160 }
1161 }
1162 /* }}} */
1163
1164 /* {{{ Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1165 PHP_FUNCTION(current)
1166 {
1167 zval *array_zv;
1168 zval *entry;
1169
1170 ZEND_PARSE_PARAMETERS_START(1, 1)
1171 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1172 ZEND_PARSE_PARAMETERS_END();
1173
1174 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1175 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1176 RETURN_FALSE;
1177 }
1178
1179 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1180 entry = Z_INDIRECT_P(entry);
1181 }
1182
1183 RETURN_COPY_DEREF(entry);
1184 }
1185 /* }}} */
1186
1187 /* {{{ Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1188 PHP_FUNCTION(key)
1189 {
1190 zval *array_zv;
1191
1192 ZEND_PARSE_PARAMETERS_START(1, 1)
1193 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1194 ZEND_PARSE_PARAMETERS_END();
1195
1196 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1197 zend_hash_get_current_key_zval(array, return_value);
1198 }
1199 /* }}} */
1200
php_data_compare(const void * f,const void * s)1201 static int php_data_compare(const void *f, const void *s) /* {{{ */
1202 {
1203 return zend_compare((zval*)f, (zval*)s);
1204 }
1205 /* }}} */
1206
1207 /* {{{
1208 * proto mixed min(array values)
1209 * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1210 Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1211 PHP_FUNCTION(min)
1212 {
1213 uint32_t argc;
1214 zval *args = NULL;
1215
1216 ZEND_PARSE_PARAMETERS_START(1, -1)
1217 Z_PARAM_VARIADIC('+', args, argc)
1218 ZEND_PARSE_PARAMETERS_END();
1219
1220 /* mixed min ( array $values ) */
1221 if (argc == 1) {
1222 if (Z_TYPE(args[0]) != IS_ARRAY) {
1223 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1224 RETURN_THROWS();
1225 } else {
1226 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0);
1227 if (result) {
1228 RETURN_COPY_DEREF(result);
1229 } else {
1230 zend_argument_value_error(1, "must contain at least one element");
1231 RETURN_THROWS();
1232 }
1233 }
1234 } else {
1235 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1236 zval *min;
1237 uint32_t i;
1238
1239 min = &args[0];
1240 zend_long min_lval;
1241 double min_dval;
1242
1243 if (Z_TYPE_P(min) == IS_LONG) {
1244 min_lval = Z_LVAL_P(min);
1245
1246 for (i = 1; i < argc; i++) {
1247 if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1248 if (min_lval > Z_LVAL(args[i])) {
1249 min_lval = Z_LVAL(args[i]);
1250 min = &args[i];
1251 }
1252 } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) {
1253 /* if min_lval can be exactly represented as a double, go to double dedicated code */
1254 min_dval = (double) min_lval;
1255 goto double_compare;
1256 } else {
1257 goto generic_compare;
1258 }
1259 }
1260
1261 RETURN_LONG(min_lval);
1262 } else if (Z_TYPE_P(min) == IS_DOUBLE) {
1263 min_dval = Z_DVAL_P(min);
1264
1265 for (i = 1; i < argc; i++) {
1266 if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1267 double_compare:
1268 if (min_dval > Z_DVAL(args[i])) {
1269 min_dval = Z_DVAL(args[i]);
1270 min = &args[i];
1271 }
1272 } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1273 /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1274 if (min_dval > (double)Z_LVAL(args[i])) {
1275 min_dval = (double)Z_LVAL(args[i]);
1276 min = &args[i];
1277 }
1278 } else {
1279 goto generic_compare;
1280 }
1281 }
1282 } else {
1283 for (i = 1; i < argc; i++) {
1284 generic_compare:
1285 if (zend_compare(&args[i], min) < 0) {
1286 min = &args[i];
1287 }
1288 }
1289 }
1290
1291 RETURN_COPY(min);
1292 }
1293 }
1294 /* }}} */
1295
1296 /* {{{
1297 * proto mixed max(array values)
1298 * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1299 Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)1300 PHP_FUNCTION(max)
1301 {
1302 zval *args = NULL;
1303 uint32_t argc;
1304
1305 ZEND_PARSE_PARAMETERS_START(1, -1)
1306 Z_PARAM_VARIADIC('+', args, argc)
1307 ZEND_PARSE_PARAMETERS_END();
1308
1309 /* mixed max ( array $values ) */
1310 if (argc == 1) {
1311 if (Z_TYPE(args[0]) != IS_ARRAY) {
1312 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1313 RETURN_THROWS();
1314 } else {
1315 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1);
1316 if (result) {
1317 RETURN_COPY_DEREF(result);
1318 } else {
1319 zend_argument_value_error(1, "must contain at least one element");
1320 RETURN_THROWS();
1321 }
1322 }
1323 } else {
1324 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1325 zval *max;
1326 uint32_t i;
1327
1328 max = &args[0];
1329 zend_long max_lval;
1330 double max_dval;
1331
1332 if (Z_TYPE_P(max) == IS_LONG) {
1333 max_lval = Z_LVAL_P(max);
1334
1335 for (i = 1; i < argc; i++) {
1336 if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1337 if (max_lval < Z_LVAL(args[i])) {
1338 max_lval = Z_LVAL(args[i]);
1339 max = &args[i];
1340 }
1341 } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) {
1342 /* if max_lval can be exactly represented as a double, go to double dedicated code */
1343 max_dval = (double) max_lval;
1344 goto double_compare;
1345 } else {
1346 goto generic_compare;
1347 }
1348 }
1349
1350 RETURN_LONG(max_lval);
1351 } else if (Z_TYPE_P(max) == IS_DOUBLE) {
1352 max_dval = Z_DVAL_P(max);
1353
1354 for (i = 1; i < argc; i++) {
1355 if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1356 double_compare:
1357 if (max_dval < Z_DVAL(args[i])) {
1358 max_dval = Z_DVAL(args[i]);
1359 max = &args[i];
1360 }
1361 } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1362 /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1363 if (max_dval < (double)Z_LVAL(args[i])) {
1364 max_dval = (double)Z_LVAL(args[i]);
1365 max = &args[i];
1366 }
1367 } else {
1368 goto generic_compare;
1369 }
1370 }
1371 } else {
1372 for (i = 1; i < argc; i++) {
1373 generic_compare:
1374 if (zend_compare(&args[i], max) > 0) {
1375 max = &args[i];
1376 }
1377 }
1378 }
1379
1380 RETURN_COPY(max);
1381 }
1382 }
1383 /* }}} */
1384
1385 typedef struct {
1386 zend_fcall_info fci;
1387 zend_fcall_info_cache fci_cache;
1388 } php_array_walk_context;
1389
php_array_walk(php_array_walk_context * context,zval * array,zval * userdata,bool recursive)1390 static zend_result php_array_walk(
1391 php_array_walk_context *context, zval *array, zval *userdata, bool recursive)
1392 {
1393 zval args[3], /* Arguments to userland function */
1394 retval, /* Return value - unused */
1395 *zv;
1396 HashTable *target_hash = HASH_OF(array);
1397 HashPosition pos;
1398 uint32_t ht_iter;
1399 zend_result result = SUCCESS;
1400
1401 /* Create a local copy of fci, as we want to use different arguments at different
1402 * levels of recursion. */
1403 zend_fcall_info fci = context->fci;
1404
1405 if (zend_hash_num_elements(target_hash) == 0) {
1406 return result;
1407 }
1408
1409 /* Set up known arguments */
1410 ZVAL_UNDEF(&args[1]);
1411 if (userdata) {
1412 ZVAL_COPY(&args[2], userdata);
1413 }
1414
1415 fci.retval = &retval;
1416 fci.param_count = userdata ? 3 : 2;
1417 fci.params = args;
1418
1419 zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1420 ht_iter = zend_hash_iterator_add(target_hash, pos);
1421
1422 /* Iterate through hash */
1423 do {
1424 /* Retrieve value */
1425 zv = zend_hash_get_current_data_ex(target_hash, &pos);
1426 if (zv == NULL) {
1427 break;
1428 }
1429
1430 /* Skip undefined indirect elements */
1431 if (Z_TYPE_P(zv) == IS_INDIRECT) {
1432 zv = Z_INDIRECT_P(zv);
1433 if (Z_TYPE_P(zv) == IS_UNDEF) {
1434 zend_hash_move_forward_ex(target_hash, &pos);
1435 continue;
1436 }
1437
1438 /* Add type source for property references. */
1439 if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1440 zend_property_info *prop_info =
1441 zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1442 if (prop_info) {
1443 ZVAL_NEW_REF(zv, zv);
1444 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1445 }
1446 }
1447 }
1448
1449 /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1450 ZVAL_MAKE_REF(zv);
1451
1452 /* Retrieve key */
1453 zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1454
1455 /* Move to next element already now -- this mirrors the approach used by foreach
1456 * and ensures proper behavior with regard to modifications. */
1457 zend_hash_move_forward_ex(target_hash, &pos);
1458
1459 /* Back up hash position, as it may change */
1460 EG(ht_iterators)[ht_iter].pos = pos;
1461
1462 if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1463 HashTable *thash;
1464 zval ref;
1465 ZVAL_COPY_VALUE(&ref, zv);
1466
1467 ZVAL_DEREF(zv);
1468 SEPARATE_ARRAY(zv);
1469 thash = Z_ARRVAL_P(zv);
1470 if (GC_IS_RECURSIVE(thash)) {
1471 zend_throw_error(NULL, "Recursion detected");
1472 result = FAILURE;
1473 break;
1474 }
1475
1476 Z_ADDREF(ref);
1477 GC_PROTECT_RECURSION(thash);
1478 result = php_array_walk(context, zv, userdata, recursive);
1479 if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1480 /* If the hashtable changed in the meantime, we'll "leak" this apply count
1481 * increment -- our reference to thash is no longer valid. */
1482 GC_UNPROTECT_RECURSION(thash);
1483 }
1484 zval_ptr_dtor(&ref);
1485 } else {
1486 ZVAL_COPY(&args[0], zv);
1487
1488 /* Call the userland function */
1489 result = zend_call_function(&fci, &context->fci_cache);
1490 if (result == SUCCESS) {
1491 zval_ptr_dtor(&retval);
1492 }
1493
1494 zval_ptr_dtor(&args[0]);
1495 }
1496
1497 if (Z_TYPE(args[1]) != IS_UNDEF) {
1498 zval_ptr_dtor(&args[1]);
1499 ZVAL_UNDEF(&args[1]);
1500 }
1501
1502 if (result == FAILURE) {
1503 break;
1504 }
1505
1506 /* Reload array and position -- both may have changed */
1507 if (Z_TYPE_P(array) == IS_ARRAY) {
1508 pos = zend_hash_iterator_pos_ex(ht_iter, array);
1509 target_hash = Z_ARRVAL_P(array);
1510 } else if (Z_TYPE_P(array) == IS_OBJECT) {
1511 target_hash = Z_OBJPROP_P(array);
1512 pos = zend_hash_iterator_pos(ht_iter, target_hash);
1513 } else {
1514 zend_type_error("Iterated value is no longer an array or object");
1515 result = FAILURE;
1516 break;
1517 }
1518 } while (!EG(exception));
1519
1520 if (userdata) {
1521 zval_ptr_dtor(&args[2]);
1522 }
1523 zend_hash_iterator_del(ht_iter);
1524 return result;
1525 }
1526
1527 /* {{{ Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1528 PHP_FUNCTION(array_walk)
1529 {
1530 zval *array;
1531 zval *userdata = NULL;
1532 php_array_walk_context context;
1533
1534 ZEND_PARSE_PARAMETERS_START(2, 3)
1535 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1536 Z_PARAM_FUNC(context.fci, context.fci_cache)
1537 Z_PARAM_OPTIONAL
1538 Z_PARAM_ZVAL(userdata)
1539 ZEND_PARSE_PARAMETERS_END();
1540
1541 php_array_walk(&context, array, userdata, /* recursive */ false);
1542 RETURN_TRUE;
1543 }
1544 /* }}} */
1545
1546 /* {{{ Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1547 PHP_FUNCTION(array_walk_recursive)
1548 {
1549 zval *array;
1550 zval *userdata = NULL;
1551 php_array_walk_context context;
1552
1553 ZEND_PARSE_PARAMETERS_START(2, 3)
1554 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1555 Z_PARAM_FUNC(context.fci, context.fci_cache)
1556 Z_PARAM_OPTIONAL
1557 Z_PARAM_ZVAL(userdata)
1558 ZEND_PARSE_PARAMETERS_END();
1559
1560 php_array_walk(&context, array, userdata, /* recursive */ true);
1561 RETURN_TRUE;
1562 }
1563 /* }}} */
1564
1565 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1566 * 0 = return boolean
1567 * 1 = return key
1568 */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1569 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1570 {
1571 zval *value, /* value to check for */
1572 *array, /* array to check in */
1573 *entry; /* pointer to array entry */
1574 zend_ulong num_idx;
1575 zend_string *str_idx;
1576 bool strict = 0; /* strict comparison or not */
1577
1578 ZEND_PARSE_PARAMETERS_START(2, 3)
1579 Z_PARAM_ZVAL(value)
1580 Z_PARAM_ARRAY(array)
1581 Z_PARAM_OPTIONAL
1582 Z_PARAM_BOOL(strict)
1583 ZEND_PARSE_PARAMETERS_END();
1584
1585 if (strict) {
1586 if (Z_TYPE_P(value) == IS_LONG) {
1587 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1588 ZVAL_DEREF(entry);
1589 if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1590 if (behavior == 0) {
1591 RETURN_TRUE;
1592 } else {
1593 if (str_idx) {
1594 RETURN_STR_COPY(str_idx);
1595 } else {
1596 RETURN_LONG(num_idx);
1597 }
1598 }
1599 }
1600 } ZEND_HASH_FOREACH_END();
1601 } else {
1602 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1603 ZVAL_DEREF(entry);
1604 if (fast_is_identical_function(value, entry)) {
1605 if (behavior == 0) {
1606 RETURN_TRUE;
1607 } else {
1608 if (str_idx) {
1609 RETURN_STR_COPY(str_idx);
1610 } else {
1611 RETURN_LONG(num_idx);
1612 }
1613 }
1614 }
1615 } ZEND_HASH_FOREACH_END();
1616 }
1617 } else {
1618 if (Z_TYPE_P(value) == IS_LONG) {
1619 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1620 if (fast_equal_check_long(value, entry)) {
1621 if (behavior == 0) {
1622 RETURN_TRUE;
1623 } else {
1624 if (str_idx) {
1625 RETURN_STR_COPY(str_idx);
1626 } else {
1627 RETURN_LONG(num_idx);
1628 }
1629 }
1630 }
1631 } ZEND_HASH_FOREACH_END();
1632 } else if (Z_TYPE_P(value) == IS_STRING) {
1633 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1634 if (fast_equal_check_string(value, entry)) {
1635 if (behavior == 0) {
1636 RETURN_TRUE;
1637 } else {
1638 if (str_idx) {
1639 RETURN_STR_COPY(str_idx);
1640 } else {
1641 RETURN_LONG(num_idx);
1642 }
1643 }
1644 }
1645 } ZEND_HASH_FOREACH_END();
1646 } else {
1647 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1648 if (fast_equal_check_function(value, entry)) {
1649 if (behavior == 0) {
1650 RETURN_TRUE;
1651 } else {
1652 if (str_idx) {
1653 RETURN_STR_COPY(str_idx);
1654 } else {
1655 RETURN_LONG(num_idx);
1656 }
1657 }
1658 }
1659 } ZEND_HASH_FOREACH_END();
1660 }
1661 }
1662
1663 RETURN_FALSE;
1664 }
1665 /* }}} */
1666
1667 /* {{{ Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1668 PHP_FUNCTION(in_array)
1669 {
1670 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1671 }
1672 /* }}} */
1673
1674 /* {{{ Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1675 PHP_FUNCTION(array_search)
1676 {
1677 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1678 }
1679 /* }}} */
1680
php_valid_var_name(const char * var_name,size_t var_name_len)1681 static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
1682 {
1683 #if 1
1684 /* first 256 bits for first character, and second 256 bits for the next */
1685 static const uint32_t charset[8] = {
1686 /* 31 0 63 32 95 64 127 96 */
1687 0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1688 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1689 static const uint32_t charset2[8] = {
1690 /* 31 0 63 32 95 64 127 96 */
1691 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1692 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1693 #endif
1694 size_t i;
1695 uint32_t ch;
1696
1697 if (UNEXPECTED(!var_name_len)) {
1698 return 0;
1699 }
1700
1701 /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1702 ch = (uint32_t)((unsigned char *)var_name)[0];
1703 #if 1
1704 if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1705 #else
1706 if (var_name[0] != '_' &&
1707 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1708 (ch < 97 /* a */ || /* z */ ch > 122) &&
1709 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1710 ) {
1711 #endif
1712 return 0;
1713 }
1714
1715 /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1716 if (var_name_len > 1) {
1717 i = 1;
1718 do {
1719 ch = (uint32_t)((unsigned char *)var_name)[i];
1720 #if 1
1721 if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1722 #else
1723 if (var_name[i] != '_' &&
1724 (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
1725 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1726 (ch < 97 /* a */ || /* z */ ch > 122) &&
1727 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1728 ) {
1729 #endif
1730 return 0;
1731 }
1732 } while (++i < var_name_len);
1733 }
1734 return 1;
1735 }
1736 /* }}} */
1737
1738 PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore) /* {{{ */
1739 {
1740 ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1741 memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
1742
1743 if (add_underscore) {
1744 Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
1745 }
1746
1747 memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1748
1749 return SUCCESS;
1750 }
1751 /* }}} */
1752
1753 static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1754 {
1755 zend_long count = 0;
1756 zend_string *var_name;
1757 zval *entry, *orig_var;
1758
1759 if (HT_IS_PACKED(arr)) {
1760 return 0;
1761 }
1762 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1763 if (!var_name) {
1764 continue;
1765 }
1766 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1767 if (orig_var) {
1768 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1769 orig_var = Z_INDIRECT_P(orig_var);
1770 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1771 continue;
1772 }
1773 }
1774 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1775 continue;
1776 }
1777 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1778 continue;
1779 }
1780 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1781 zend_throw_error(NULL, "Cannot re-assign $this");
1782 return -1;
1783 }
1784 if (Z_ISREF_P(entry)) {
1785 Z_ADDREF_P(entry);
1786 } else {
1787 ZVAL_MAKE_REF_EX(entry, 2);
1788 }
1789 zval_ptr_dtor(orig_var);
1790 ZVAL_REF(orig_var, Z_REF_P(entry));
1791 count++;
1792 }
1793 } ZEND_HASH_FOREACH_END();
1794
1795 return count;
1796 }
1797 /* }}} */
1798
1799 static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1800 {
1801 zend_long count = 0;
1802 zend_string *var_name;
1803 zval *entry, *orig_var;
1804
1805 if (HT_IS_PACKED(arr)) {
1806 return 0;
1807 }
1808 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1809 if (!var_name) {
1810 continue;
1811 }
1812 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1813 if (orig_var) {
1814 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1815 orig_var = Z_INDIRECT_P(orig_var);
1816 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1817 continue;
1818 }
1819 }
1820 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1821 continue;
1822 }
1823 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1824 continue;
1825 }
1826 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1827 zend_throw_error(NULL, "Cannot re-assign $this");
1828 return -1;
1829 }
1830 ZVAL_DEREF(entry);
1831 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1832 if (UNEXPECTED(EG(exception))) {
1833 return -1;
1834 }
1835 count++;
1836 }
1837 } ZEND_HASH_FOREACH_END();
1838
1839 return count;
1840 }
1841 /* }}} */
1842
1843 static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1844 {
1845 zend_long count = 0;
1846 zend_string *var_name;
1847 zval *entry, *orig_var;
1848
1849 if (HT_IS_PACKED(arr)) {
1850 return 0;
1851 }
1852 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1853 if (!var_name) {
1854 continue;
1855 }
1856 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1857 continue;
1858 }
1859 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1860 zend_throw_error(NULL, "Cannot re-assign $this");
1861 return -1;
1862 }
1863 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1864 if (orig_var) {
1865 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1866 orig_var = Z_INDIRECT_P(orig_var);
1867 }
1868 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1869 continue;
1870 }
1871 if (Z_ISREF_P(entry)) {
1872 Z_ADDREF_P(entry);
1873 } else {
1874 ZVAL_MAKE_REF_EX(entry, 2);
1875 }
1876 zval_ptr_dtor(orig_var);
1877 ZVAL_REF(orig_var, Z_REF_P(entry));
1878 } else {
1879 if (Z_ISREF_P(entry)) {
1880 Z_ADDREF_P(entry);
1881 } else {
1882 ZVAL_MAKE_REF_EX(entry, 2);
1883 }
1884 zend_hash_add_new(symbol_table, var_name, entry);
1885 }
1886 count++;
1887 } ZEND_HASH_FOREACH_END();
1888
1889 return count;
1890 }
1891 /* }}} */
1892
1893 static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1894 {
1895 zend_long count = 0;
1896 zend_string *var_name;
1897 zval *entry, *orig_var;
1898
1899 if (HT_IS_PACKED(arr)) {
1900 return 0;
1901 }
1902 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1903 if (!var_name) {
1904 continue;
1905 }
1906 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1907 continue;
1908 }
1909 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1910 zend_throw_error(NULL, "Cannot re-assign $this");
1911 return -1;
1912 }
1913 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1914 if (orig_var) {
1915 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1916 orig_var = Z_INDIRECT_P(orig_var);
1917 }
1918 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1919 continue;
1920 }
1921 ZVAL_DEREF(entry);
1922 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1923 if (UNEXPECTED(EG(exception))) {
1924 return -1;
1925 }
1926 } else {
1927 ZVAL_DEREF(entry);
1928 Z_TRY_ADDREF_P(entry);
1929 zend_hash_add_new(symbol_table, var_name, entry);
1930 }
1931 count++;
1932 } ZEND_HASH_FOREACH_END();
1933
1934 return count;
1935 }
1936 /* }}} */
1937
1938 static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1939 {
1940 zend_long count = 0;
1941 zend_string *var_name;
1942 zval *entry, *orig_var, final_name;
1943
1944 if (HT_IS_PACKED(arr)) {
1945 return 0;
1946 }
1947 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1948 if (!var_name) {
1949 continue;
1950 }
1951 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1952 if (orig_var) {
1953 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1954 orig_var = Z_INDIRECT_P(orig_var);
1955 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1956 if (Z_ISREF_P(entry)) {
1957 Z_ADDREF_P(entry);
1958 } else {
1959 ZVAL_MAKE_REF_EX(entry, 2);
1960 }
1961 ZVAL_REF(orig_var, Z_REF_P(entry));
1962 count++;
1963 continue;
1964 }
1965 }
1966 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1967 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1968 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
1969 zend_throw_error(NULL, "Cannot re-assign $this");
1970 return -1;
1971 } else {
1972 if (Z_ISREF_P(entry)) {
1973 Z_ADDREF_P(entry);
1974 } else {
1975 ZVAL_MAKE_REF_EX(entry, 2);
1976 }
1977 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1978 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1979 orig_var = Z_INDIRECT_P(orig_var);
1980 }
1981 zval_ptr_dtor(orig_var);
1982 ZVAL_REF(orig_var, Z_REF_P(entry));
1983 } else {
1984 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1985 }
1986 count++;
1987 }
1988 }
1989 zval_ptr_dtor_str(&final_name);
1990 }
1991 } ZEND_HASH_FOREACH_END();
1992
1993 return count;
1994 }
1995 /* }}} */
1996
1997 static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1998 {
1999 zend_long count = 0;
2000 zend_string *var_name;
2001 zval *entry, *orig_var, final_name;
2002
2003 if (HT_IS_PACKED(arr)) {
2004 return 0;
2005 }
2006 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2007 if (!var_name) {
2008 continue;
2009 }
2010 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2011 if (orig_var) {
2012 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2013 orig_var = Z_INDIRECT_P(orig_var);
2014 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2015 ZVAL_COPY_DEREF(orig_var, entry);
2016 count++;
2017 continue;
2018 }
2019 }
2020 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2021 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2022 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2023 zend_throw_error(NULL, "Cannot re-assign $this");
2024 return -1;
2025 } else {
2026 ZVAL_DEREF(entry);
2027 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2028 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2029 orig_var = Z_INDIRECT_P(orig_var);
2030 }
2031 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2032 if (UNEXPECTED(EG(exception))) {
2033 zend_string_release_ex(Z_STR(final_name), 0);
2034 return -1;
2035 }
2036 } else {
2037 Z_TRY_ADDREF_P(entry);
2038 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2039 }
2040 count++;
2041 }
2042 }
2043 zval_ptr_dtor_str(&final_name);
2044 }
2045 } ZEND_HASH_FOREACH_END();
2046
2047 return count;
2048 }
2049 /* }}} */
2050
2051 static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2052 {
2053 zend_long count = 0;
2054 zend_string *var_name;
2055 zval *entry, *orig_var, final_name;
2056
2057 if (HT_IS_PACKED(arr)) {
2058 return 0;
2059 }
2060 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2061 if (!var_name) {
2062 continue;
2063 }
2064 if (ZSTR_LEN(var_name) == 0) {
2065 continue;
2066 }
2067 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2068 if (orig_var) {
2069 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2070 orig_var = Z_INDIRECT_P(orig_var);
2071 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2072 if (Z_ISREF_P(entry)) {
2073 Z_ADDREF_P(entry);
2074 } else {
2075 ZVAL_MAKE_REF_EX(entry, 2);
2076 }
2077 ZVAL_REF(orig_var, Z_REF_P(entry));
2078 count++;
2079 continue;
2080 }
2081 }
2082 prefix:
2083 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2084 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2085 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2086 zend_throw_error(NULL, "Cannot re-assign $this");
2087 return -1;
2088 } else {
2089 if (Z_ISREF_P(entry)) {
2090 Z_ADDREF_P(entry);
2091 } else {
2092 ZVAL_MAKE_REF_EX(entry, 2);
2093 }
2094 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2095 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2096 orig_var = Z_INDIRECT_P(orig_var);
2097 }
2098 zval_ptr_dtor(orig_var);
2099 ZVAL_REF(orig_var, Z_REF_P(entry));
2100 } else {
2101 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2102 }
2103 count++;
2104 }
2105 }
2106 zval_ptr_dtor_str(&final_name);
2107 } else {
2108 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2109 continue;
2110 }
2111 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2112 goto prefix;
2113 }
2114 if (Z_ISREF_P(entry)) {
2115 Z_ADDREF_P(entry);
2116 } else {
2117 ZVAL_MAKE_REF_EX(entry, 2);
2118 }
2119 zend_hash_add_new(symbol_table, var_name, entry);
2120 count++;
2121 }
2122 } ZEND_HASH_FOREACH_END();
2123
2124 return count;
2125 }
2126 /* }}} */
2127
2128 static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2129 {
2130 zend_long count = 0;
2131 zend_string *var_name;
2132 zval *entry, *orig_var, final_name;
2133
2134 if (HT_IS_PACKED(arr)) {
2135 return 0;
2136 }
2137 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2138 if (!var_name) {
2139 continue;
2140 }
2141 if (ZSTR_LEN(var_name) == 0) {
2142 continue;
2143 }
2144 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2145 if (orig_var) {
2146 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2147 orig_var = Z_INDIRECT_P(orig_var);
2148 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2149 ZVAL_COPY_DEREF(orig_var, entry);
2150 count++;
2151 continue;
2152 }
2153 }
2154 prefix:
2155 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2156 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2157 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2158 zend_throw_error(NULL, "Cannot re-assign $this");
2159 return -1;
2160 } else {
2161 ZVAL_DEREF(entry);
2162 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2163 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2164 orig_var = Z_INDIRECT_P(orig_var);
2165 }
2166 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2167 if (UNEXPECTED(EG(exception))) {
2168 zend_string_release_ex(Z_STR(final_name), 0);
2169 return -1;
2170 }
2171 } else {
2172 Z_TRY_ADDREF_P(entry);
2173 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2174 }
2175 count++;
2176 }
2177 }
2178 zval_ptr_dtor_str(&final_name);
2179 } else {
2180 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2181 continue;
2182 }
2183 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2184 goto prefix;
2185 }
2186 ZVAL_DEREF(entry);
2187 Z_TRY_ADDREF_P(entry);
2188 zend_hash_add_new(symbol_table, var_name, entry);
2189 count++;
2190 }
2191 } ZEND_HASH_FOREACH_END();
2192
2193 return count;
2194 }
2195 /* }}} */
2196
2197 static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2198 {
2199 zend_long count = 0;
2200 zend_string *var_name;
2201 zend_ulong num_key;
2202 zval *entry, *orig_var, final_name;
2203
2204 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2205 if (var_name) {
2206 if (ZSTR_LEN(var_name) == 0) {
2207 continue;
2208 }
2209 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2210 } else {
2211 zend_string *str = zend_long_to_str(num_key);
2212 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2213 zend_string_release_ex(str, 0);
2214 }
2215 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2216 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2217 zend_throw_error(NULL, "Cannot re-assign $this");
2218 return -1;
2219 } else {
2220 if (Z_ISREF_P(entry)) {
2221 Z_ADDREF_P(entry);
2222 } else {
2223 ZVAL_MAKE_REF_EX(entry, 2);
2224 }
2225 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2226 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2227 orig_var = Z_INDIRECT_P(orig_var);
2228 }
2229 zval_ptr_dtor(orig_var);
2230 ZVAL_REF(orig_var, Z_REF_P(entry));
2231 } else {
2232 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2233 }
2234 count++;
2235 }
2236 }
2237 zval_ptr_dtor_str(&final_name);
2238 } ZEND_HASH_FOREACH_END();
2239
2240 return count;
2241 }
2242 /* }}} */
2243
2244 static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2245 {
2246 zend_long count = 0;
2247 zend_string *var_name;
2248 zend_ulong num_key;
2249 zval *entry, *orig_var, final_name;
2250
2251 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2252 if (var_name) {
2253 if (ZSTR_LEN(var_name) == 0) {
2254 continue;
2255 }
2256 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2257 } else {
2258 zend_string *str = zend_long_to_str(num_key);
2259 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2260 zend_string_release_ex(str, 0);
2261 }
2262 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2263 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2264 zend_throw_error(NULL, "Cannot re-assign $this");
2265 return -1;
2266 } else {
2267 ZVAL_DEREF(entry);
2268 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2269 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2270 orig_var = Z_INDIRECT_P(orig_var);
2271 }
2272 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2273 if (UNEXPECTED(EG(exception))) {
2274 zend_string_release_ex(Z_STR(final_name), 0);
2275 return -1;
2276 }
2277 } else {
2278 Z_TRY_ADDREF_P(entry);
2279 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2280 }
2281 count++;
2282 }
2283 }
2284 zval_ptr_dtor_str(&final_name);
2285 } ZEND_HASH_FOREACH_END();
2286
2287 return count;
2288 }
2289 /* }}} */
2290
2291 static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2292 {
2293 zend_long count = 0;
2294 zend_string *var_name;
2295 zend_ulong num_key;
2296 zval *entry, *orig_var, final_name;
2297
2298 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2299 if (var_name) {
2300 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2301 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2302 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2303 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2304 zval_ptr_dtor_str(&final_name);
2305 continue;
2306 }
2307 } else {
2308 ZVAL_STR_COPY(&final_name, var_name);
2309 }
2310 } else {
2311 zend_string *str = zend_long_to_str(num_key);
2312 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2313 zend_string_release_ex(str, 0);
2314 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2315 zval_ptr_dtor_str(&final_name);
2316 continue;
2317 }
2318 }
2319 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2320 zend_throw_error(NULL, "Cannot re-assign $this");
2321 return -1;
2322 } else {
2323 if (Z_ISREF_P(entry)) {
2324 Z_ADDREF_P(entry);
2325 } else {
2326 ZVAL_MAKE_REF_EX(entry, 2);
2327 }
2328 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2329 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2330 orig_var = Z_INDIRECT_P(orig_var);
2331 }
2332 zval_ptr_dtor(orig_var);
2333 ZVAL_REF(orig_var, Z_REF_P(entry));
2334 } else {
2335 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2336 }
2337 count++;
2338 }
2339 zval_ptr_dtor_str(&final_name);
2340 } ZEND_HASH_FOREACH_END();
2341
2342 return count;
2343 }
2344 /* }}} */
2345
2346 static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2347 {
2348 zend_long count = 0;
2349 zend_string *var_name;
2350 zend_ulong num_key;
2351 zval *entry, *orig_var, final_name;
2352
2353 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2354 if (var_name) {
2355 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2356 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2357 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2358 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2359 zval_ptr_dtor_str(&final_name);
2360 continue;
2361 }
2362 } else {
2363 ZVAL_STR_COPY(&final_name, var_name);
2364 }
2365 } else {
2366 zend_string *str = zend_long_to_str(num_key);
2367 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2368 zend_string_release_ex(str, 0);
2369 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2370 zval_ptr_dtor_str(&final_name);
2371 continue;
2372 }
2373 }
2374 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2375 zend_throw_error(NULL, "Cannot re-assign $this");
2376 return -1;
2377 } else {
2378 ZVAL_DEREF(entry);
2379 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2380 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2381 orig_var = Z_INDIRECT_P(orig_var);
2382 }
2383 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2384 if (UNEXPECTED(EG(exception))) {
2385 zend_string_release_ex(Z_STR(final_name), 0);
2386 return -1;
2387 }
2388 } else {
2389 Z_TRY_ADDREF_P(entry);
2390 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2391 }
2392 count++;
2393 }
2394 zval_ptr_dtor_str(&final_name);
2395 } ZEND_HASH_FOREACH_END();
2396
2397 return count;
2398 }
2399 /* }}} */
2400
2401 static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2402 {
2403 zend_long count = 0;
2404 zend_string *var_name;
2405 zval *entry, *orig_var;
2406
2407 if (HT_IS_PACKED(arr)) {
2408 return 0;
2409 }
2410 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2411 if (!var_name) {
2412 continue;
2413 }
2414 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2415 continue;
2416 }
2417 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2418 continue;
2419 }
2420 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2421 if (orig_var) {
2422 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2423 orig_var = Z_INDIRECT_P(orig_var);
2424 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2425 if (Z_ISREF_P(entry)) {
2426 Z_ADDREF_P(entry);
2427 } else {
2428 ZVAL_MAKE_REF_EX(entry, 2);
2429 }
2430 ZVAL_REF(orig_var, Z_REF_P(entry));
2431 count++;
2432 }
2433 }
2434 } else {
2435 if (Z_ISREF_P(entry)) {
2436 Z_ADDREF_P(entry);
2437 } else {
2438 ZVAL_MAKE_REF_EX(entry, 2);
2439 }
2440 zend_hash_add_new(symbol_table, var_name, entry);
2441 count++;
2442 }
2443 } ZEND_HASH_FOREACH_END();
2444
2445 return count;
2446 }
2447 /* }}} */
2448
2449 static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2450 {
2451 zend_long count = 0;
2452 zend_string *var_name;
2453 zval *entry, *orig_var;
2454
2455 if (HT_IS_PACKED(arr)) {
2456 return 0;
2457 }
2458 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2459 if (!var_name) {
2460 continue;
2461 }
2462 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2463 continue;
2464 }
2465 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2466 continue;
2467 }
2468 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2469 if (orig_var) {
2470 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2471 orig_var = Z_INDIRECT_P(orig_var);
2472 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2473 ZVAL_COPY_DEREF(orig_var, entry);
2474 count++;
2475 }
2476 }
2477 } else {
2478 ZVAL_DEREF(entry);
2479 Z_TRY_ADDREF_P(entry);
2480 zend_hash_add_new(symbol_table, var_name, entry);
2481 count++;
2482 }
2483 } ZEND_HASH_FOREACH_END();
2484
2485 return count;
2486 }
2487 /* }}} */
2488
2489 /* {{{ Imports variables into symbol table from an array */
2490 PHP_FUNCTION(extract)
2491 {
2492 zval *var_array_param;
2493 zend_long extract_refs;
2494 zend_long extract_type = PHP_EXTR_OVERWRITE;
2495 zend_string *prefix = NULL;
2496 zend_long count;
2497 zend_array *symbol_table;
2498
2499 ZEND_PARSE_PARAMETERS_START(1, 3)
2500 Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2501 Z_PARAM_OPTIONAL
2502 Z_PARAM_LONG(extract_type)
2503 Z_PARAM_STR(prefix)
2504 ZEND_PARSE_PARAMETERS_END();
2505
2506 extract_refs = (extract_type & PHP_EXTR_REFS);
2507 if (extract_refs) {
2508 SEPARATE_ARRAY(var_array_param);
2509 }
2510 extract_type &= 0xff;
2511
2512 if (extract_type < PHP_EXTR_OVERWRITE || extract_type > PHP_EXTR_IF_EXISTS) {
2513 zend_argument_value_error(2, "must be a valid extract type");
2514 RETURN_THROWS();
2515 }
2516
2517 if (extract_type > PHP_EXTR_SKIP && extract_type <= PHP_EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2518 zend_argument_value_error(3, "is required when using this extract type");
2519 RETURN_THROWS();
2520 }
2521
2522 if (prefix) {
2523 if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
2524 zend_argument_value_error(3, "must be a valid identifier");
2525 RETURN_THROWS();
2526 }
2527 }
2528
2529 if (zend_forbid_dynamic_call() == FAILURE) {
2530 return;
2531 }
2532
2533 symbol_table = zend_rebuild_symbol_table();
2534 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2535
2536 if (extract_refs) {
2537 switch (extract_type) {
2538 case PHP_EXTR_IF_EXISTS:
2539 count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2540 break;
2541 case PHP_EXTR_OVERWRITE:
2542 count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2543 break;
2544 case PHP_EXTR_PREFIX_IF_EXISTS:
2545 count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2546 break;
2547 case PHP_EXTR_PREFIX_SAME:
2548 count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2549 break;
2550 case PHP_EXTR_PREFIX_ALL:
2551 count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2552 break;
2553 case PHP_EXTR_PREFIX_INVALID:
2554 count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2555 break;
2556 default:
2557 count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2558 break;
2559 }
2560 } else {
2561 /* The array might be stored in a local variable that will be overwritten */
2562 zval array_copy;
2563 ZVAL_COPY(&array_copy, var_array_param);
2564 switch (extract_type) {
2565 case PHP_EXTR_IF_EXISTS:
2566 count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2567 break;
2568 case PHP_EXTR_OVERWRITE:
2569 count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2570 break;
2571 case PHP_EXTR_PREFIX_IF_EXISTS:
2572 count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2573 break;
2574 case PHP_EXTR_PREFIX_SAME:
2575 count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2576 break;
2577 case PHP_EXTR_PREFIX_ALL:
2578 count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2579 break;
2580 case PHP_EXTR_PREFIX_INVALID:
2581 count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2582 break;
2583 default:
2584 count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2585 break;
2586 }
2587 zval_ptr_dtor(&array_copy);
2588 }
2589
2590 RETURN_LONG(count);
2591 }
2592 /* }}} */
2593
2594 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2595 {
2596 zval *value_ptr, data;
2597
2598 ZVAL_DEREF(entry);
2599 if (Z_TYPE_P(entry) == IS_STRING) {
2600 if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2601 ZVAL_DEREF(value_ptr);
2602 Z_TRY_ADDREF_P(value_ptr);
2603 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2604 } else if (zend_string_equals(Z_STR_P(entry), ZSTR_KNOWN(ZEND_STR_THIS))) {
2605 zend_object *object = zend_get_this_object(EG(current_execute_data));
2606 if (object) {
2607 ZVAL_OBJ_COPY(&data, object);
2608 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2609 }
2610 } else {
2611 php_error_docref(NULL, E_WARNING, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
2612 }
2613 } else if (Z_TYPE_P(entry) == IS_ARRAY) {
2614 if (Z_REFCOUNTED_P(entry)) {
2615 if (Z_IS_RECURSIVE_P(entry)) {
2616 zend_throw_error(NULL, "Recursion detected");
2617 return;
2618 }
2619 Z_PROTECT_RECURSION_P(entry);
2620 }
2621 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
2622 php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
2623 } ZEND_HASH_FOREACH_END();
2624 if (Z_REFCOUNTED_P(entry)) {
2625 Z_UNPROTECT_RECURSION_P(entry);
2626 }
2627 } else {
2628 php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2629 return;
2630 }
2631 }
2632 /* }}} */
2633
2634 /* {{{ Creates a hash containing variables and their values */
2635 PHP_FUNCTION(compact)
2636 {
2637 zval *args = NULL; /* function arguments array */
2638 uint32_t num_args, i;
2639 zend_array *symbol_table;
2640
2641 ZEND_PARSE_PARAMETERS_START(1, -1)
2642 Z_PARAM_VARIADIC('+', args, num_args)
2643 ZEND_PARSE_PARAMETERS_END();
2644
2645 if (zend_forbid_dynamic_call() == FAILURE) {
2646 return;
2647 }
2648
2649 symbol_table = zend_rebuild_symbol_table();
2650 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2651
2652 /* compact() is probably most used with a single array of var_names
2653 or multiple string names, rather than a combination of both.
2654 So quickly guess a minimum result size based on that */
2655 if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2656 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2657 } else {
2658 array_init_size(return_value, num_args);
2659 }
2660
2661 for (i = 0; i < num_args; i++) {
2662 php_compact_var(symbol_table, return_value, &args[i], i + 1);
2663 }
2664 }
2665 /* }}} */
2666
2667 /* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2668 PHP_FUNCTION(array_fill)
2669 {
2670 zval *val;
2671 zend_long start_key, num;
2672
2673 ZEND_PARSE_PARAMETERS_START(3, 3)
2674 Z_PARAM_LONG(start_key)
2675 Z_PARAM_LONG(num)
2676 Z_PARAM_ZVAL(val)
2677 ZEND_PARSE_PARAMETERS_END();
2678
2679 if (EXPECTED(num > 0)) {
2680 if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2681 zend_argument_value_error(2, "is too large");
2682 RETURN_THROWS();
2683 } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2684 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2685 RETURN_THROWS();
2686 } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2687 /* create packed array */
2688 zval *zv;
2689
2690 array_init_size(return_value, (uint32_t)(start_key + num));
2691 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2692 Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2693 Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2694 Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2695
2696 if (Z_REFCOUNTED_P(val)) {
2697 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2698 }
2699
2700 zv = Z_ARRVAL_P(return_value)->arPacked;
2701
2702 while (start_key--) {
2703 ZVAL_UNDEF(zv);
2704 zv++;
2705 }
2706 while (num--) {
2707 ZVAL_COPY_VALUE(zv, val);
2708 zv++;
2709 }
2710 } else {
2711 /* create hash */
2712 array_init_size(return_value, (uint32_t)num);
2713 zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2714 if (Z_REFCOUNTED_P(val)) {
2715 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2716 }
2717 zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2718 while (--num) {
2719 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2720 start_key++;
2721 }
2722 }
2723 } else if (EXPECTED(num == 0)) {
2724 RETURN_EMPTY_ARRAY();
2725 } else {
2726 zend_argument_value_error(2, "must be greater than or equal to 0");
2727 RETURN_THROWS();
2728 }
2729 }
2730 /* }}} */
2731
2732 /* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2733 PHP_FUNCTION(array_fill_keys)
2734 {
2735 zval *keys, *val, *entry;
2736
2737 ZEND_PARSE_PARAMETERS_START(2, 2)
2738 Z_PARAM_ARRAY(keys)
2739 Z_PARAM_ZVAL(val)
2740 ZEND_PARSE_PARAMETERS_END();
2741
2742 /* Initialize return array */
2743 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2744
2745 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2746 ZVAL_DEREF(entry);
2747 Z_TRY_ADDREF_P(val);
2748 if (Z_TYPE_P(entry) == IS_LONG) {
2749 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2750 } else {
2751 zend_string *tmp_key;
2752 zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2753 zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2754 zend_tmp_string_release(tmp_key);
2755 }
2756 } ZEND_HASH_FOREACH_END();
2757 }
2758 /* }}} */
2759
2760 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2761 double __calc_size = ((start - end) / (_step)) + 1; \
2762 if (__calc_size >= (double)HT_MAX_SIZE) { \
2763 zend_value_error(\
2764 "The supplied range exceeds the maximum array size: start=%0.1f end=%0.1f step=%0.1f", end, start, (_step)); \
2765 RETURN_THROWS(); \
2766 } \
2767 size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2768 array_init_size(return_value, size); \
2769 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2770 } while (0)
2771
2772 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2773 zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2774 if (__calc_size >= HT_MAX_SIZE - 1) { \
2775 zend_value_error(\
2776 "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT " step=" ZEND_LONG_FMT, end, start, (_step)); \
2777 RETURN_THROWS(); \
2778 } \
2779 size = (uint32_t)(__calc_size + 1); \
2780 array_init_size(return_value, size); \
2781 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2782 } while (0)
2783
2784 /* Process input for the range() function
2785 * 0 on exceptions
2786 * IS_LONG if only interpretable as int
2787 * IS_DOUBLE if only interpretable as float
2788 * IS_STRING if only interpretable as string
2789 * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
2790 static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2791 {
2792 switch (Z_TYPE_P(input)) {
2793 case IS_LONG:
2794 *lval = Z_LVAL_P(input);
2795 *dval = (double) Z_LVAL_P(input);
2796 return IS_LONG;
2797 case IS_DOUBLE:
2798 *dval = Z_DVAL_P(input);
2799 check_dval_value:
2800 if (zend_isinf(*dval)) {
2801 zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2802 return 0;
2803 }
2804 if (zend_isnan(*dval)) {
2805 zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2806 return 0;
2807 }
2808 return IS_DOUBLE;
2809 case IS_STRING: {
2810 /* Process strings:
2811 * - Empty strings are converted to 0 with a diagnostic
2812 * - Check if string is numeric and store the values in passed pointer
2813 * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2814 * - If numeric int, check it is one byte or not
2815 * - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2816 * - If not should only be interpreted as int, return IS_LONG;
2817 * - Otherwise is a string and return IS_STRING */
2818 if (Z_STRLEN_P(input) == 0) {
2819 const char *arg_name = get_active_function_arg_name(arg_num);
2820 php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2821 if (UNEXPECTED(EG(exception))) {
2822 return 0;
2823 }
2824 *lval = 0;
2825 *dval = 0.0;
2826 return IS_LONG;
2827 }
2828 uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2829 if (type == IS_DOUBLE) {
2830 goto check_dval_value;
2831 }
2832 if (type == IS_LONG) {
2833 *dval = (double) *lval;
2834 if (Z_STRLEN_P(input) == 1) {
2835 return IS_ARRAY;
2836 } else {
2837 return IS_LONG;
2838 }
2839 }
2840 if (Z_STRLEN_P(input) != 1) {
2841 const char *arg_name = get_active_function_arg_name(arg_num);
2842 php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2843 if (UNEXPECTED(EG(exception))) {
2844 return 0;
2845 }
2846 }
2847 /* Set fall back values to 0 in case the other argument is not a string */
2848 *lval = 0;
2849 *dval = 0.0;
2850 return IS_STRING;
2851 }
2852 EMPTY_SWITCH_DEFAULT_CASE();
2853 }
2854 }
2855
2856 /* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2857 PHP_FUNCTION(range)
2858 {
2859 zval *user_start, *user_end, *user_step = NULL, tmp;
2860 bool is_step_double = false;
2861 bool is_step_negative = false;
2862 double step_double = 1.0;
2863 zend_long step = 1;
2864
2865 ZEND_PARSE_PARAMETERS_START(2, 3)
2866 Z_PARAM_NUMBER_OR_STR(user_start)
2867 Z_PARAM_NUMBER_OR_STR(user_end)
2868 Z_PARAM_OPTIONAL
2869 Z_PARAM_NUMBER(user_step)
2870 ZEND_PARSE_PARAMETERS_END();
2871
2872 if (user_step) {
2873 if (UNEXPECTED(Z_TYPE_P(user_step) == IS_DOUBLE)) {
2874 step_double = Z_DVAL_P(user_step);
2875
2876 if (zend_isinf(step_double)) {
2877 zend_argument_value_error(3, "must be a finite number, INF provided");
2878 RETURN_THROWS();
2879 }
2880 if (zend_isnan(step_double)) {
2881 zend_argument_value_error(3, "must be a finite number, NAN provided");
2882 RETURN_THROWS();
2883 }
2884
2885 /* We only want positive step values. */
2886 if (step_double < 0.0) {
2887 is_step_negative = true;
2888 step_double *= -1;
2889 }
2890 step = zend_dval_to_lval(step_double);
2891 if (!zend_is_long_compatible(step_double, step)) {
2892 is_step_double = true;
2893 }
2894 } else {
2895 step = Z_LVAL_P(user_step);
2896 /* We only want positive step values. */
2897 if (step < 0) {
2898 is_step_negative = true;
2899 step *= -1;
2900 }
2901 step_double = (double) step;
2902 }
2903 if (step_double == 0.0) {
2904 zend_argument_value_error(3, "cannot be 0");
2905 RETURN_THROWS();
2906 }
2907 }
2908
2909 uint8_t start_type;
2910 double start_double;
2911 zend_long start_long;
2912 uint8_t end_type;
2913 double end_double;
2914 zend_long end_long;
2915
2916 start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2917 if (start_type == 0) {
2918 RETURN_THROWS();
2919 }
2920 end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2921 if (end_type == 0) {
2922 RETURN_THROWS();
2923 }
2924
2925 /* If the range is given as strings, generate an array of characters. */
2926 if (start_type >= IS_STRING || end_type >= IS_STRING) {
2927 /* If one of the inputs is NOT a string */
2928 if (UNEXPECTED(start_type + end_type < 2*IS_STRING)) {
2929 if (start_type < IS_STRING) {
2930 if (end_type != IS_ARRAY) {
2931 php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if"
2932 " argument #2 ($end) is a single byte string, argument #2 ($end) converted to 0");
2933 }
2934 end_type = IS_LONG;
2935 } else if (end_type < IS_STRING) {
2936 if (start_type != IS_ARRAY) {
2937 php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a single byte string if"
2938 " argument #1 ($start) is a single byte string, argument #1 ($start) converted to 0");
2939 }
2940 start_type = IS_LONG;
2941 }
2942 if (UNEXPECTED(EG(exception))) {
2943 RETURN_THROWS();
2944 }
2945 goto handle_numeric_inputs;
2946 }
2947
2948 if (is_step_double) {
2949 /* Only emit warning if one of the input is not a numeric digit */
2950 if (start_type == IS_STRING || end_type == IS_STRING) {
2951 php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2952 " of characters, inputs converted to 0");
2953 }
2954 if (UNEXPECTED(EG(exception))) {
2955 RETURN_THROWS();
2956 }
2957 end_type = IS_LONG;
2958 start_type = IS_LONG;
2959 goto handle_numeric_inputs;
2960 }
2961
2962 /* Generate array of characters */
2963 unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
2964 unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
2965
2966 /* Decreasing char range */
2967 if (low > high) {
2968 if (low - high < step) {
2969 goto boundary_error;
2970 }
2971 /* Initialize the return_value as an array. */
2972 array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
2973 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2974 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2975 for (; low >= high; low -= (unsigned int)step) {
2976 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2977 ZEND_HASH_FILL_NEXT();
2978 if (((signed int)low - step) < 0) {
2979 break;
2980 }
2981 }
2982 } ZEND_HASH_FILL_END();
2983 } else if (high > low) { /* Increasing char range */
2984 if (is_step_negative) {
2985 goto negative_step_error;
2986 }
2987 if (high - low < step) {
2988 goto boundary_error;
2989 }
2990 array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
2991 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2992 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2993 for (; low <= high; low += (unsigned int)step) {
2994 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2995 ZEND_HASH_FILL_NEXT();
2996 if (((signed int)low + step) > 255) {
2997 break;
2998 }
2999 }
3000 } ZEND_HASH_FILL_END();
3001 } else {
3002 array_init(return_value);
3003 ZVAL_CHAR(&tmp, low);
3004 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3005 }
3006 return;
3007 }
3008
3009 handle_numeric_inputs:
3010 if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
3011 double element;
3012 uint32_t i, size;
3013
3014 /* Decreasing float range */
3015 if (start_double > end_double) {
3016 if (start_double - end_double < step_double) {
3017 goto boundary_error;
3018 }
3019
3020 RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
3021
3022 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3023 for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
3024 ZEND_HASH_FILL_SET_DOUBLE(element);
3025 ZEND_HASH_FILL_NEXT();
3026 }
3027 } ZEND_HASH_FILL_END();
3028 } else if (end_double > start_double) { /* Increasing float range */
3029 if (is_step_negative) {
3030 goto negative_step_error;
3031 }
3032 if (end_double - start_double < step_double) {
3033 goto boundary_error;
3034 }
3035
3036 RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
3037
3038 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3039 for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
3040 ZEND_HASH_FILL_SET_DOUBLE(element);
3041 ZEND_HASH_FILL_NEXT();
3042 }
3043 } ZEND_HASH_FILL_END();
3044 } else {
3045 array_init(return_value);
3046 ZVAL_DOUBLE(&tmp, start_double);
3047 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3048 }
3049 } else {
3050 ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
3051 /* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
3052 zend_ulong unsigned_step= (zend_ulong)step;
3053 uint32_t i, size;
3054
3055 /* Decreasing int range */
3056 if (start_long > end_long) {
3057 if ((zend_ulong)start_long - end_long < unsigned_step) {
3058 goto boundary_error;
3059 }
3060
3061 RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3062
3063 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3064 for (i = 0; i < size; ++i) {
3065 ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3066 ZEND_HASH_FILL_NEXT();
3067 }
3068 } ZEND_HASH_FILL_END();
3069 } else if (end_long > start_long) { /* Increasing int range */
3070 if (is_step_negative) {
3071 goto negative_step_error;
3072 }
3073 if ((zend_ulong)end_long - start_long < unsigned_step) {
3074 goto boundary_error;
3075 }
3076
3077 RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3078
3079 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3080 for (i = 0; i < size; ++i) {
3081 ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3082 ZEND_HASH_FILL_NEXT();
3083 }
3084 } ZEND_HASH_FILL_END();
3085 } else {
3086 array_init(return_value);
3087 ZVAL_LONG(&tmp, start_long);
3088 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3089 }
3090 }
3091 return;
3092
3093 negative_step_error:
3094 zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3095 RETURN_THROWS();
3096
3097 boundary_error:
3098 zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3099 RETURN_THROWS();
3100 }
3101 /* }}} */
3102
3103 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
3104 #undef RANGE_CHECK_LONG_INIT_ARRAY
3105
3106 /* {{{ php_array_data_shuffle */
3107 PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array) /* {{{ */
3108 {
3109 int64_t idx, j, n_elems, rnd_idx, n_left;
3110 zval *zv, temp;
3111 HashTable *hash;
3112
3113 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3114
3115 if (n_elems < 1) {
3116 return true;
3117 }
3118
3119 hash = Z_ARRVAL_P(array);
3120 n_left = n_elems;
3121
3122 if (!HT_IS_PACKED(hash)) {
3123 if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3124 Bucket *p = hash->arData;
3125 zend_long i = hash->nNumUsed;
3126
3127 for (; i > 0; p++, i--) {
3128 if (p->key) {
3129 zend_string_release(p->key);
3130 p->key = NULL;
3131 }
3132 }
3133 }
3134 zend_hash_to_packed(hash);
3135 }
3136
3137 if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3138 if (hash->nNumUsed != hash->nNumOfElements) {
3139 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3140 zv = hash->arPacked + idx;
3141 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3142 if (j != idx) {
3143 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3144 }
3145 j++;
3146 }
3147 }
3148 while (--n_left) {
3149 rnd_idx = algo->range(status, 0, n_left);
3150 if (EG(exception)) {
3151 return false;
3152 }
3153 if (rnd_idx != n_left) {
3154 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3155 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3156 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3157 }
3158 }
3159 } else {
3160 zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3161
3162 if (hash->nNumUsed != hash->nNumOfElements) {
3163 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3164 zv = hash->arPacked + idx;
3165 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3166 if (j != idx) {
3167 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3168 if (idx == iter_pos) {
3169 zend_hash_iterators_update(hash, idx, j);
3170 iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3171 }
3172 }
3173 j++;
3174 }
3175 }
3176 while (--n_left) {
3177 rnd_idx = algo->range(status, 0, n_left);
3178 if (EG(exception)) {
3179 return false;
3180 }
3181 if (rnd_idx != n_left) {
3182 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3183 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3184 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3185 zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
3186 }
3187 }
3188 }
3189 hash->nNumUsed = n_elems;
3190 hash->nInternalPointer = 0;
3191 hash->nNextFreeElement = n_elems;
3192
3193 return true;
3194 }
3195 /* }}} */
3196
3197 /* {{{ Randomly shuffle the contents of an array */
3198 PHP_FUNCTION(shuffle)
3199 {
3200 zval *array;
3201
3202 ZEND_PARSE_PARAMETERS_START(1, 1)
3203 Z_PARAM_ARRAY_EX(array, 0, 1)
3204 ZEND_PARSE_PARAMETERS_END();
3205
3206 php_array_data_shuffle(php_random_default_algo(), php_random_default_status(), array);
3207
3208 RETURN_TRUE;
3209 }
3210 /* }}} */
3211
3212 static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
3213 {
3214 HashTable out_hash; /* Output hashtable */
3215 zend_long num_in; /* Number of entries in the input hashtable */
3216 zend_long pos; /* Current position in the hashtable */
3217 uint32_t idx;
3218 zval *entry; /* Hash entry */
3219 uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3220
3221 /* Get number of entries in the input hash */
3222 num_in = zend_hash_num_elements(in_hash);
3223
3224 /* Clamp the offset.. */
3225 if (offset > num_in) {
3226 offset = num_in;
3227 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3228 offset = 0;
3229 }
3230
3231 /* ..and the length */
3232 if (length < 0) {
3233 length = num_in - offset + length;
3234 } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3235 length = num_in - offset;
3236 }
3237
3238 /* Create and initialize output hash */
3239 zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
3240
3241 if (HT_IS_PACKED(in_hash)) {
3242 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3243 entry = in_hash->arPacked;
3244 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3245 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3246
3247 zend_hash_next_index_insert_new(&out_hash, entry);
3248 if (idx == iter_pos) {
3249 if ((zend_long)idx != pos) {
3250 zend_hash_iterators_update(in_hash, idx, pos);
3251 }
3252 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3253 }
3254 pos++;
3255 }
3256
3257 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3258 if (removed != NULL) {
3259 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3260 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3261 pos++;
3262 Z_TRY_ADDREF_P(entry);
3263 zend_hash_next_index_insert_new(removed, entry);
3264 zend_hash_packed_del_val(in_hash, entry);
3265 }
3266 } else { /* otherwise just skip those entries */
3267 int pos2 = pos;
3268
3269 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3270 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3271 pos2++;
3272 zend_hash_packed_del_val(in_hash, entry);
3273 }
3274 }
3275 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3276
3277 /* If there are entries to insert.. */
3278 if (replace) {
3279 ZEND_HASH_FOREACH_VAL(replace, entry) {
3280 Z_TRY_ADDREF_P(entry);
3281 zend_hash_next_index_insert_new(&out_hash, entry);
3282 pos++;
3283 } ZEND_HASH_FOREACH_END();
3284 }
3285
3286 /* Copy the remaining input hash entries to the output hash */
3287 entry = in_hash->arPacked + idx;
3288 for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3289 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3290 zend_hash_next_index_insert_new(&out_hash, entry);
3291 if (idx == iter_pos) {
3292 if ((zend_long)idx != pos) {
3293 zend_hash_iterators_update(in_hash, idx, pos);
3294 }
3295 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3296 }
3297 pos++;
3298 }
3299 } else {
3300 Bucket *p = in_hash->arData;
3301
3302 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3303 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, p++) {
3304 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3305 entry = &p->val;
3306
3307 /* Update output hash depending on key type */
3308 if (p->key == NULL) {
3309 zend_hash_next_index_insert_new(&out_hash, entry);
3310 } else {
3311 zend_hash_add_new(&