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 - s2;
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 - s2;
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_array_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
709 } else {
710 zend_array_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_array_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_array_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_array_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_array_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 arr = zend_array_dup(arr);
906
907 zend_array_sort(arr, compare_func, renumber);
908
909 zval garbage;
910 ZVAL_COPY_VALUE(&garbage, array);
911 ZVAL_ARR(array, arr);
912 zval_ptr_dtor(&garbage);
913
914 PHP_ARRAY_CMP_FUNC_RESTORE();
915 RETURN_TRUE;
916 }
917 /* }}} */
918
919 /* {{{ Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)920 PHP_FUNCTION(usort)
921 {
922 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
923 }
924 /* }}} */
925
926 /* {{{ Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)927 PHP_FUNCTION(uasort)
928 {
929 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
930 }
931 /* }}} */
932
php_array_user_key_compare_unstable(Bucket * f,Bucket * s)933 static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
934 {
935 zval args[2];
936 zval retval;
937 bool call_failed;
938
939 if (f->key == NULL) {
940 ZVAL_LONG(&args[0], f->h);
941 } else {
942 ZVAL_STR_COPY(&args[0], f->key);
943 }
944 if (s->key == NULL) {
945 ZVAL_LONG(&args[1], s->h);
946 } else {
947 ZVAL_STR_COPY(&args[1], s->key);
948 }
949
950 BG(user_compare_fci).param_count = 2;
951 BG(user_compare_fci).params = args;
952 BG(user_compare_fci).retval = &retval;
953 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
954 zval_ptr_dtor(&args[1]);
955 zval_ptr_dtor(&args[0]);
956 if (UNEXPECTED(call_failed)) {
957 return 0;
958 }
959
960 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
961 if (!ARRAYG(compare_deprecation_thrown)) {
962 php_error_docref(NULL, E_DEPRECATED,
963 "Returning bool from comparison function is deprecated, "
964 "return an integer less than, equal to, or greater than zero");
965 ARRAYG(compare_deprecation_thrown) = 1;
966 }
967
968 if (Z_TYPE(retval) == IS_FALSE) {
969 /* Retry with swapped operands. */
970 if (s->key == NULL) {
971 ZVAL_LONG(&args[0], s->h);
972 } else {
973 ZVAL_STR_COPY(&args[0], s->key);
974 }
975 if (f->key == NULL) {
976 ZVAL_LONG(&args[1], f->h);
977 } else {
978 ZVAL_STR_COPY(&args[1], f->key);
979 }
980
981 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
982 zval_ptr_dtor(&args[1]);
983 zval_ptr_dtor(&args[0]);
984 if (call_failed) {
985 return 0;
986 }
987
988 zend_long ret = zval_get_long(&retval);
989 zval_ptr_dtor(&retval);
990 return -ZEND_NORMALIZE_BOOL(ret);
991 }
992 }
993
994 zend_long result = zval_get_long(&retval);
995 zval_ptr_dtor(&retval);
996 return ZEND_NORMALIZE_BOOL(result);
997 }
998 /* }}} */
999
php_array_user_key_compare(Bucket * a,Bucket * b)1000 static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */
1001 {
1002 RETURN_STABLE_SORT(a, b, php_array_user_key_compare_unstable(a, b));
1003 }
1004 /* }}} */
1005
1006 /* {{{ Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)1007 PHP_FUNCTION(uksort)
1008 {
1009 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1010 }
1011 /* }}} */
1012
get_ht_for_iap(zval * zv,bool separate)1013 static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
1014 if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
1015 return Z_ARRVAL_P(zv);
1016 }
1017
1018 ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
1019 php_error_docref(NULL, E_DEPRECATED,
1020 "Calling %s() on an object is deprecated", get_active_function_name());
1021
1022 zend_object *zobj = Z_OBJ_P(zv);
1023 if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
1024 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
1025 GC_DELREF(zobj->properties);
1026 }
1027 zobj->properties = zend_array_dup(zobj->properties);
1028 }
1029 return zobj->handlers->get_properties(zobj);
1030 }
1031
1032 /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1033 PHP_FUNCTION(end)
1034 {
1035 zval *array_zv;
1036 zval *entry;
1037
1038 ZEND_PARSE_PARAMETERS_START(1, 1)
1039 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1040 ZEND_PARSE_PARAMETERS_END();
1041
1042 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1043 if (zend_hash_num_elements(array) == 0) {
1044 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1045 RETURN_FALSE;
1046 }
1047 zend_hash_internal_pointer_end(array);
1048
1049 if (USED_RET()) {
1050 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1051 RETURN_FALSE;
1052 }
1053
1054 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1055 entry = Z_INDIRECT_P(entry);
1056 }
1057
1058 RETURN_COPY_DEREF(entry);
1059 }
1060 }
1061 /* }}} */
1062
1063 /* {{{ Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1064 PHP_FUNCTION(prev)
1065 {
1066 zval *array_zv;
1067 zval *entry;
1068
1069 ZEND_PARSE_PARAMETERS_START(1, 1)
1070 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1071 ZEND_PARSE_PARAMETERS_END();
1072
1073 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1074 if (zend_hash_num_elements(array) == 0) {
1075 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1076 RETURN_FALSE;
1077 }
1078 zend_hash_move_backwards(array);
1079
1080 if (USED_RET()) {
1081 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1082 RETURN_FALSE;
1083 }
1084
1085 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1086 entry = Z_INDIRECT_P(entry);
1087 }
1088
1089 RETURN_COPY_DEREF(entry);
1090 }
1091 }
1092 /* }}} */
1093
1094 /* {{{ Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1095 PHP_FUNCTION(next)
1096 {
1097 zval *array_zv;
1098 zval *entry;
1099
1100 ZEND_PARSE_PARAMETERS_START(1, 1)
1101 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1102 ZEND_PARSE_PARAMETERS_END();
1103
1104 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1105 if (zend_hash_num_elements(array) == 0) {
1106 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1107 RETURN_FALSE;
1108 }
1109 zend_hash_move_forward(array);
1110
1111 if (USED_RET()) {
1112 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1113 RETURN_FALSE;
1114 }
1115
1116 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1117 entry = Z_INDIRECT_P(entry);
1118 }
1119
1120 RETURN_COPY_DEREF(entry);
1121 }
1122 }
1123 /* }}} */
1124
1125 /* {{{ Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1126 PHP_FUNCTION(reset)
1127 {
1128 zval *array_zv;
1129 zval *entry;
1130
1131 ZEND_PARSE_PARAMETERS_START(1, 1)
1132 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1133 ZEND_PARSE_PARAMETERS_END();
1134
1135 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1136 if (zend_hash_num_elements(array) == 0) {
1137 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1138 RETURN_FALSE;
1139 }
1140 zend_hash_internal_pointer_reset(array);
1141
1142 if (USED_RET()) {
1143 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1144 RETURN_FALSE;
1145 }
1146
1147 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1148 entry = Z_INDIRECT_P(entry);
1149 }
1150
1151 RETURN_COPY_DEREF(entry);
1152 }
1153 }
1154 /* }}} */
1155
1156 /* {{{ Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1157 PHP_FUNCTION(current)
1158 {
1159 zval *array_zv;
1160 zval *entry;
1161
1162 ZEND_PARSE_PARAMETERS_START(1, 1)
1163 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1164 ZEND_PARSE_PARAMETERS_END();
1165
1166 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1167 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1168 RETURN_FALSE;
1169 }
1170
1171 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1172 entry = Z_INDIRECT_P(entry);
1173 }
1174
1175 RETURN_COPY_DEREF(entry);
1176 }
1177 /* }}} */
1178
1179 /* {{{ Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1180 PHP_FUNCTION(key)
1181 {
1182 zval *array_zv;
1183
1184 ZEND_PARSE_PARAMETERS_START(1, 1)
1185 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1186 ZEND_PARSE_PARAMETERS_END();
1187
1188 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1189 zend_hash_get_current_key_zval(array, return_value);
1190 }
1191 /* }}} */
1192
php_data_compare(const void * f,const void * s)1193 static int php_data_compare(const void *f, const void *s) /* {{{ */
1194 {
1195 return zend_compare((zval*)f, (zval*)s);
1196 }
1197 /* }}} */
1198
1199 /* {{{
1200 * proto mixed min(array values)
1201 * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1202 Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1203 PHP_FUNCTION(min)
1204 {
1205 uint32_t argc;
1206 zval *args = NULL;
1207
1208 ZEND_PARSE_PARAMETERS_START(1, -1)
1209 Z_PARAM_VARIADIC('+', args, argc)
1210 ZEND_PARSE_PARAMETERS_END();
1211
1212 /* mixed min ( array $values ) */
1213 if (argc == 1) {
1214 if (Z_TYPE(args[0]) != IS_ARRAY) {
1215 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1216 RETURN_THROWS();
1217 } else {
1218 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0);
1219 if (result) {
1220 RETURN_COPY_DEREF(result);
1221 } else {
1222 zend_argument_value_error(1, "must contain at least one element");
1223 RETURN_THROWS();
1224 }
1225 }
1226 } else {
1227 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1228 zval *min;
1229 uint32_t i;
1230
1231 min = &args[0];
1232 zend_long min_lval;
1233 double min_dval;
1234
1235 if (Z_TYPE_P(min) == IS_LONG) {
1236 min_lval = Z_LVAL_P(min);
1237
1238 for (i = 1; i < argc; i++) {
1239 if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1240 if (min_lval > Z_LVAL(args[i])) {
1241 min_lval = Z_LVAL(args[i]);
1242 min = &args[i];
1243 }
1244 } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) {
1245 /* if min_lval can be exactly represented as a double, go to double dedicated code */
1246 min_dval = (double) min_lval;
1247 goto double_compare;
1248 } else {
1249 goto generic_compare;
1250 }
1251 }
1252
1253 RETURN_LONG(min_lval);
1254 } else if (Z_TYPE_P(min) == IS_DOUBLE) {
1255 min_dval = Z_DVAL_P(min);
1256
1257 for (i = 1; i < argc; i++) {
1258 if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1259 double_compare:
1260 if (min_dval > Z_DVAL(args[i])) {
1261 min_dval = Z_DVAL(args[i]);
1262 min = &args[i];
1263 }
1264 } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1265 /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1266 if (min_dval > (double)Z_LVAL(args[i])) {
1267 min_dval = (double)Z_LVAL(args[i]);
1268 min = &args[i];
1269 }
1270 } else {
1271 goto generic_compare;
1272 }
1273 }
1274 } else {
1275 for (i = 1; i < argc; i++) {
1276 generic_compare:
1277 if (zend_compare(&args[i], min) < 0) {
1278 min = &args[i];
1279 }
1280 }
1281 }
1282
1283 RETURN_COPY(min);
1284 }
1285 }
1286 /* }}} */
1287
1288 /* {{{
1289 * proto mixed max(array values)
1290 * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1291 Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)1292 PHP_FUNCTION(max)
1293 {
1294 zval *args = NULL;
1295 uint32_t argc;
1296
1297 ZEND_PARSE_PARAMETERS_START(1, -1)
1298 Z_PARAM_VARIADIC('+', args, argc)
1299 ZEND_PARSE_PARAMETERS_END();
1300
1301 /* mixed max ( array $values ) */
1302 if (argc == 1) {
1303 if (Z_TYPE(args[0]) != IS_ARRAY) {
1304 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1305 RETURN_THROWS();
1306 } else {
1307 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1);
1308 if (result) {
1309 RETURN_COPY_DEREF(result);
1310 } else {
1311 zend_argument_value_error(1, "must contain at least one element");
1312 RETURN_THROWS();
1313 }
1314 }
1315 } else {
1316 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1317 zval *max;
1318 uint32_t i;
1319
1320 max = &args[0];
1321 zend_long max_lval;
1322 double max_dval;
1323
1324 if (Z_TYPE_P(max) == IS_LONG) {
1325 max_lval = Z_LVAL_P(max);
1326
1327 for (i = 1; i < argc; i++) {
1328 if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1329 if (max_lval < Z_LVAL(args[i])) {
1330 max_lval = Z_LVAL(args[i]);
1331 max = &args[i];
1332 }
1333 } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) {
1334 /* if max_lval can be exactly represented as a double, go to double dedicated code */
1335 max_dval = (double) max_lval;
1336 goto double_compare;
1337 } else {
1338 goto generic_compare;
1339 }
1340 }
1341
1342 RETURN_LONG(max_lval);
1343 } else if (Z_TYPE_P(max) == IS_DOUBLE) {
1344 max_dval = Z_DVAL_P(max);
1345
1346 for (i = 1; i < argc; i++) {
1347 if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1348 double_compare:
1349 if (max_dval < Z_DVAL(args[i])) {
1350 max_dval = Z_DVAL(args[i]);
1351 max = &args[i];
1352 }
1353 } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1354 /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1355 if (max_dval < (double)Z_LVAL(args[i])) {
1356 max_dval = (double)Z_LVAL(args[i]);
1357 max = &args[i];
1358 }
1359 } else {
1360 goto generic_compare;
1361 }
1362 }
1363 } else {
1364 for (i = 1; i < argc; i++) {
1365 generic_compare:
1366 if (zend_compare(&args[i], max) > 0) {
1367 max = &args[i];
1368 }
1369 }
1370 }
1371
1372 RETURN_COPY(max);
1373 }
1374 }
1375 /* }}} */
1376
1377 typedef struct {
1378 zend_fcall_info fci;
1379 zend_fcall_info_cache fci_cache;
1380 } php_array_walk_context;
1381
php_array_walk(php_array_walk_context * context,zval * array,zval * userdata,bool recursive)1382 static zend_result php_array_walk(
1383 php_array_walk_context *context, zval *array, zval *userdata, bool recursive)
1384 {
1385 zval args[3], /* Arguments to userland function */
1386 retval, /* Return value - unused */
1387 *zv;
1388 HashTable *target_hash = HASH_OF(array);
1389 HashPosition pos;
1390 uint32_t ht_iter;
1391 zend_result result = SUCCESS;
1392
1393 /* Create a local copy of fci, as we want to use different arguments at different
1394 * levels of recursion. */
1395 zend_fcall_info fci = context->fci;
1396
1397 if (zend_hash_num_elements(target_hash) == 0) {
1398 return result;
1399 }
1400
1401 /* Set up known arguments */
1402 ZVAL_UNDEF(&args[1]);
1403 if (userdata) {
1404 ZVAL_COPY(&args[2], userdata);
1405 }
1406
1407 fci.retval = &retval;
1408 fci.param_count = userdata ? 3 : 2;
1409 fci.params = args;
1410
1411 zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1412 ht_iter = zend_hash_iterator_add(target_hash, pos);
1413
1414 /* Iterate through hash */
1415 do {
1416 /* Retrieve value */
1417 zv = zend_hash_get_current_data_ex(target_hash, &pos);
1418 if (zv == NULL) {
1419 break;
1420 }
1421
1422 /* Skip undefined indirect elements */
1423 if (Z_TYPE_P(zv) == IS_INDIRECT) {
1424 zv = Z_INDIRECT_P(zv);
1425 if (Z_TYPE_P(zv) == IS_UNDEF) {
1426 zend_hash_move_forward_ex(target_hash, &pos);
1427 continue;
1428 }
1429
1430 /* Add type source for property references. */
1431 if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1432 zend_property_info *prop_info =
1433 zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1434 if (prop_info) {
1435 ZVAL_NEW_REF(zv, zv);
1436 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1437 }
1438 }
1439 }
1440
1441 /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1442 ZVAL_MAKE_REF(zv);
1443
1444 /* Retrieve key */
1445 zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1446
1447 /* Move to next element already now -- this mirrors the approach used by foreach
1448 * and ensures proper behavior with regard to modifications. */
1449 zend_hash_move_forward_ex(target_hash, &pos);
1450
1451 /* Back up hash position, as it may change */
1452 EG(ht_iterators)[ht_iter].pos = pos;
1453
1454 if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1455 HashTable *thash;
1456 zval ref;
1457 ZVAL_COPY_VALUE(&ref, zv);
1458
1459 ZVAL_DEREF(zv);
1460 SEPARATE_ARRAY(zv);
1461 thash = Z_ARRVAL_P(zv);
1462 if (GC_IS_RECURSIVE(thash)) {
1463 zend_throw_error(NULL, "Recursion detected");
1464 result = FAILURE;
1465 break;
1466 }
1467
1468 Z_ADDREF(ref);
1469 GC_PROTECT_RECURSION(thash);
1470 result = php_array_walk(context, zv, userdata, recursive);
1471 if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1472 /* If the hashtable changed in the meantime, we'll "leak" this apply count
1473 * increment -- our reference to thash is no longer valid. */
1474 GC_UNPROTECT_RECURSION(thash);
1475 }
1476 zval_ptr_dtor(&ref);
1477 } else {
1478 ZVAL_COPY(&args[0], zv);
1479
1480 /* Call the userland function */
1481 result = zend_call_function(&fci, &context->fci_cache);
1482 if (result == SUCCESS) {
1483 zval_ptr_dtor(&retval);
1484 }
1485
1486 zval_ptr_dtor(&args[0]);
1487 }
1488
1489 if (Z_TYPE(args[1]) != IS_UNDEF) {
1490 zval_ptr_dtor(&args[1]);
1491 ZVAL_UNDEF(&args[1]);
1492 }
1493
1494 if (result == FAILURE) {
1495 break;
1496 }
1497
1498 /* Reload array and position -- both may have changed */
1499 if (Z_TYPE_P(array) == IS_ARRAY) {
1500 pos = zend_hash_iterator_pos_ex(ht_iter, array);
1501 target_hash = Z_ARRVAL_P(array);
1502 } else if (Z_TYPE_P(array) == IS_OBJECT) {
1503 target_hash = Z_OBJPROP_P(array);
1504 pos = zend_hash_iterator_pos(ht_iter, target_hash);
1505 } else {
1506 zend_type_error("Iterated value is no longer an array or object");
1507 result = FAILURE;
1508 break;
1509 }
1510 } while (!EG(exception));
1511
1512 if (userdata) {
1513 zval_ptr_dtor(&args[2]);
1514 }
1515 zend_hash_iterator_del(ht_iter);
1516 return result;
1517 }
1518
1519 /* {{{ Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1520 PHP_FUNCTION(array_walk)
1521 {
1522 zval *array;
1523 zval *userdata = NULL;
1524 php_array_walk_context context;
1525
1526 ZEND_PARSE_PARAMETERS_START(2, 3)
1527 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1528 Z_PARAM_FUNC(context.fci, context.fci_cache)
1529 Z_PARAM_OPTIONAL
1530 Z_PARAM_ZVAL(userdata)
1531 ZEND_PARSE_PARAMETERS_END();
1532
1533 php_array_walk(&context, array, userdata, /* recursive */ false);
1534 RETURN_TRUE;
1535 }
1536 /* }}} */
1537
1538 /* {{{ Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1539 PHP_FUNCTION(array_walk_recursive)
1540 {
1541 zval *array;
1542 zval *userdata = NULL;
1543 php_array_walk_context context;
1544
1545 ZEND_PARSE_PARAMETERS_START(2, 3)
1546 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1547 Z_PARAM_FUNC(context.fci, context.fci_cache)
1548 Z_PARAM_OPTIONAL
1549 Z_PARAM_ZVAL(userdata)
1550 ZEND_PARSE_PARAMETERS_END();
1551
1552 php_array_walk(&context, array, userdata, /* recursive */ true);
1553 RETURN_TRUE;
1554 }
1555 /* }}} */
1556
1557 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1558 * 0 = return boolean
1559 * 1 = return key
1560 */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1561 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1562 {
1563 zval *value, /* value to check for */
1564 *array, /* array to check in */
1565 *entry; /* pointer to array entry */
1566 zend_ulong num_idx;
1567 zend_string *str_idx;
1568 bool strict = 0; /* strict comparison or not */
1569
1570 ZEND_PARSE_PARAMETERS_START(2, 3)
1571 Z_PARAM_ZVAL(value)
1572 Z_PARAM_ARRAY(array)
1573 Z_PARAM_OPTIONAL
1574 Z_PARAM_BOOL(strict)
1575 ZEND_PARSE_PARAMETERS_END();
1576
1577 if (strict) {
1578 if (Z_TYPE_P(value) == IS_LONG) {
1579 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1580 ZVAL_DEREF(entry);
1581 if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1582 if (behavior == 0) {
1583 RETURN_TRUE;
1584 } else {
1585 if (str_idx) {
1586 RETURN_STR_COPY(str_idx);
1587 } else {
1588 RETURN_LONG(num_idx);
1589 }
1590 }
1591 }
1592 } ZEND_HASH_FOREACH_END();
1593 } else {
1594 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1595 ZVAL_DEREF(entry);
1596 if (fast_is_identical_function(value, entry)) {
1597 if (behavior == 0) {
1598 RETURN_TRUE;
1599 } else {
1600 if (str_idx) {
1601 RETURN_STR_COPY(str_idx);
1602 } else {
1603 RETURN_LONG(num_idx);
1604 }
1605 }
1606 }
1607 } ZEND_HASH_FOREACH_END();
1608 }
1609 } else {
1610 if (Z_TYPE_P(value) == IS_LONG) {
1611 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1612 if (fast_equal_check_long(value, entry)) {
1613 if (behavior == 0) {
1614 RETURN_TRUE;
1615 } else {
1616 if (str_idx) {
1617 RETURN_STR_COPY(str_idx);
1618 } else {
1619 RETURN_LONG(num_idx);
1620 }
1621 }
1622 }
1623 } ZEND_HASH_FOREACH_END();
1624 } else if (Z_TYPE_P(value) == IS_STRING) {
1625 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1626 if (fast_equal_check_string(value, entry)) {
1627 if (behavior == 0) {
1628 RETURN_TRUE;
1629 } else {
1630 if (str_idx) {
1631 RETURN_STR_COPY(str_idx);
1632 } else {
1633 RETURN_LONG(num_idx);
1634 }
1635 }
1636 }
1637 } ZEND_HASH_FOREACH_END();
1638 } else {
1639 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1640 if (fast_equal_check_function(value, entry)) {
1641 if (behavior == 0) {
1642 RETURN_TRUE;
1643 } else {
1644 if (str_idx) {
1645 RETURN_STR_COPY(str_idx);
1646 } else {
1647 RETURN_LONG(num_idx);
1648 }
1649 }
1650 }
1651 } ZEND_HASH_FOREACH_END();
1652 }
1653 }
1654
1655 RETURN_FALSE;
1656 }
1657 /* }}} */
1658
1659 /* {{{ Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1660 PHP_FUNCTION(in_array)
1661 {
1662 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1663 }
1664 /* }}} */
1665
1666 /* {{{ Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1667 PHP_FUNCTION(array_search)
1668 {
1669 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1670 }
1671 /* }}} */
1672
php_valid_var_name(const char * var_name,size_t var_name_len)1673 static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
1674 {
1675 #if 1
1676 /* first 256 bits for first character, and second 256 bits for the next */
1677 static const uint32_t charset[8] = {
1678 /* 31 0 63 32 95 64 127 96 */
1679 0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1680 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1681 static const uint32_t charset2[8] = {
1682 /* 31 0 63 32 95 64 127 96 */
1683 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1684 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1685 #endif
1686 size_t i;
1687 uint32_t ch;
1688
1689 if (UNEXPECTED(!var_name_len)) {
1690 return 0;
1691 }
1692
1693 /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1694 ch = (uint32_t)((unsigned char *)var_name)[0];
1695 #if 1
1696 if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1697 #else
1698 if (var_name[0] != '_' &&
1699 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1700 (ch < 97 /* a */ || /* z */ ch > 122) &&
1701 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1702 ) {
1703 #endif
1704 return 0;
1705 }
1706
1707 /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1708 if (var_name_len > 1) {
1709 i = 1;
1710 do {
1711 ch = (uint32_t)((unsigned char *)var_name)[i];
1712 #if 1
1713 if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1714 #else
1715 if (var_name[i] != '_' &&
1716 (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
1717 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1718 (ch < 97 /* a */ || /* z */ ch > 122) &&
1719 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1720 ) {
1721 #endif
1722 return 0;
1723 }
1724 } while (++i < var_name_len);
1725 }
1726 return 1;
1727 }
1728 /* }}} */
1729
1730 PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore) /* {{{ */
1731 {
1732 ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1733 memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
1734
1735 if (add_underscore) {
1736 Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
1737 }
1738
1739 memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1740
1741 return SUCCESS;
1742 }
1743 /* }}} */
1744
1745 static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1746 {
1747 zend_long count = 0;
1748 zend_string *var_name;
1749 zval *entry, *orig_var;
1750
1751 if (HT_IS_PACKED(arr)) {
1752 return 0;
1753 }
1754 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1755 if (!var_name) {
1756 continue;
1757 }
1758 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1759 if (orig_var) {
1760 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1761 orig_var = Z_INDIRECT_P(orig_var);
1762 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1763 continue;
1764 }
1765 }
1766 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1767 continue;
1768 }
1769 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1770 continue;
1771 }
1772 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1773 zend_throw_error(NULL, "Cannot re-assign $this");
1774 return -1;
1775 }
1776 if (Z_ISREF_P(entry)) {
1777 Z_ADDREF_P(entry);
1778 } else {
1779 ZVAL_MAKE_REF_EX(entry, 2);
1780 }
1781 zval_ptr_dtor(orig_var);
1782 ZVAL_REF(orig_var, Z_REF_P(entry));
1783 count++;
1784 }
1785 } ZEND_HASH_FOREACH_END();
1786
1787 return count;
1788 }
1789 /* }}} */
1790
1791 static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1792 {
1793 zend_long count = 0;
1794 zend_string *var_name;
1795 zval *entry, *orig_var;
1796
1797 if (HT_IS_PACKED(arr)) {
1798 return 0;
1799 }
1800 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1801 if (!var_name) {
1802 continue;
1803 }
1804 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1805 if (orig_var) {
1806 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1807 orig_var = Z_INDIRECT_P(orig_var);
1808 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1809 continue;
1810 }
1811 }
1812 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1813 continue;
1814 }
1815 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1816 continue;
1817 }
1818 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1819 zend_throw_error(NULL, "Cannot re-assign $this");
1820 return -1;
1821 }
1822 ZVAL_DEREF(entry);
1823 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1824 if (UNEXPECTED(EG(exception))) {
1825 return -1;
1826 }
1827 count++;
1828 }
1829 } ZEND_HASH_FOREACH_END();
1830
1831 return count;
1832 }
1833 /* }}} */
1834
1835 static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1836 {
1837 zend_long count = 0;
1838 zend_string *var_name;
1839 zval *entry, *orig_var;
1840
1841 if (HT_IS_PACKED(arr)) {
1842 return 0;
1843 }
1844 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1845 if (!var_name) {
1846 continue;
1847 }
1848 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1849 continue;
1850 }
1851 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1852 zend_throw_error(NULL, "Cannot re-assign $this");
1853 return -1;
1854 }
1855 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1856 if (orig_var) {
1857 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1858 orig_var = Z_INDIRECT_P(orig_var);
1859 }
1860 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1861 continue;
1862 }
1863 if (Z_ISREF_P(entry)) {
1864 Z_ADDREF_P(entry);
1865 } else {
1866 ZVAL_MAKE_REF_EX(entry, 2);
1867 }
1868 zval_ptr_dtor(orig_var);
1869 ZVAL_REF(orig_var, Z_REF_P(entry));
1870 } else {
1871 if (Z_ISREF_P(entry)) {
1872 Z_ADDREF_P(entry);
1873 } else {
1874 ZVAL_MAKE_REF_EX(entry, 2);
1875 }
1876 zend_hash_add_new(symbol_table, var_name, entry);
1877 }
1878 count++;
1879 } ZEND_HASH_FOREACH_END();
1880
1881 return count;
1882 }
1883 /* }}} */
1884
1885 static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1886 {
1887 zend_long count = 0;
1888 zend_string *var_name;
1889 zval *entry, *orig_var;
1890
1891 if (HT_IS_PACKED(arr)) {
1892 return 0;
1893 }
1894 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1895 if (!var_name) {
1896 continue;
1897 }
1898 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1899 continue;
1900 }
1901 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1902 zend_throw_error(NULL, "Cannot re-assign $this");
1903 return -1;
1904 }
1905 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1906 if (orig_var) {
1907 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1908 orig_var = Z_INDIRECT_P(orig_var);
1909 }
1910 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1911 continue;
1912 }
1913 ZVAL_DEREF(entry);
1914 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1915 if (UNEXPECTED(EG(exception))) {
1916 return -1;
1917 }
1918 } else {
1919 ZVAL_DEREF(entry);
1920 Z_TRY_ADDREF_P(entry);
1921 zend_hash_add_new(symbol_table, var_name, entry);
1922 }
1923 count++;
1924 } ZEND_HASH_FOREACH_END();
1925
1926 return count;
1927 }
1928 /* }}} */
1929
1930 static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1931 {
1932 zend_long count = 0;
1933 zend_string *var_name;
1934 zval *entry, *orig_var, final_name;
1935
1936 if (HT_IS_PACKED(arr)) {
1937 return 0;
1938 }
1939 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1940 if (!var_name) {
1941 continue;
1942 }
1943 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1944 if (orig_var) {
1945 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1946 orig_var = Z_INDIRECT_P(orig_var);
1947 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1948 if (Z_ISREF_P(entry)) {
1949 Z_ADDREF_P(entry);
1950 } else {
1951 ZVAL_MAKE_REF_EX(entry, 2);
1952 }
1953 ZVAL_REF(orig_var, Z_REF_P(entry));
1954 count++;
1955 continue;
1956 }
1957 }
1958 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1959 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1960 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
1961 zend_throw_error(NULL, "Cannot re-assign $this");
1962 return -1;
1963 } else {
1964 if (Z_ISREF_P(entry)) {
1965 Z_ADDREF_P(entry);
1966 } else {
1967 ZVAL_MAKE_REF_EX(entry, 2);
1968 }
1969 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1970 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1971 orig_var = Z_INDIRECT_P(orig_var);
1972 }
1973 zval_ptr_dtor(orig_var);
1974 ZVAL_REF(orig_var, Z_REF_P(entry));
1975 } else {
1976 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1977 }
1978 count++;
1979 }
1980 }
1981 zval_ptr_dtor_str(&final_name);
1982 }
1983 } ZEND_HASH_FOREACH_END();
1984
1985 return count;
1986 }
1987 /* }}} */
1988
1989 static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1990 {
1991 zend_long count = 0;
1992 zend_string *var_name;
1993 zval *entry, *orig_var, final_name;
1994
1995 if (HT_IS_PACKED(arr)) {
1996 return 0;
1997 }
1998 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1999 if (!var_name) {
2000 continue;
2001 }
2002 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2003 if (orig_var) {
2004 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2005 orig_var = Z_INDIRECT_P(orig_var);
2006 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2007 ZVAL_COPY_DEREF(orig_var, entry);
2008 count++;
2009 continue;
2010 }
2011 }
2012 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2013 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2014 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2015 zend_throw_error(NULL, "Cannot re-assign $this");
2016 return -1;
2017 } else {
2018 ZVAL_DEREF(entry);
2019 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2020 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2021 orig_var = Z_INDIRECT_P(orig_var);
2022 }
2023 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2024 if (UNEXPECTED(EG(exception))) {
2025 zend_string_release_ex(Z_STR(final_name), 0);
2026 return -1;
2027 }
2028 } else {
2029 Z_TRY_ADDREF_P(entry);
2030 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2031 }
2032 count++;
2033 }
2034 }
2035 zval_ptr_dtor_str(&final_name);
2036 }
2037 } ZEND_HASH_FOREACH_END();
2038
2039 return count;
2040 }
2041 /* }}} */
2042
2043 static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2044 {
2045 zend_long count = 0;
2046 zend_string *var_name;
2047 zval *entry, *orig_var, final_name;
2048
2049 if (HT_IS_PACKED(arr)) {
2050 return 0;
2051 }
2052 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2053 if (!var_name) {
2054 continue;
2055 }
2056 if (ZSTR_LEN(var_name) == 0) {
2057 continue;
2058 }
2059 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2060 if (orig_var) {
2061 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2062 orig_var = Z_INDIRECT_P(orig_var);
2063 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2064 if (Z_ISREF_P(entry)) {
2065 Z_ADDREF_P(entry);
2066 } else {
2067 ZVAL_MAKE_REF_EX(entry, 2);
2068 }
2069 ZVAL_REF(orig_var, Z_REF_P(entry));
2070 count++;
2071 continue;
2072 }
2073 }
2074 prefix:
2075 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2076 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2077 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2078 zend_throw_error(NULL, "Cannot re-assign $this");
2079 return -1;
2080 } else {
2081 if (Z_ISREF_P(entry)) {
2082 Z_ADDREF_P(entry);
2083 } else {
2084 ZVAL_MAKE_REF_EX(entry, 2);
2085 }
2086 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2087 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2088 orig_var = Z_INDIRECT_P(orig_var);
2089 }
2090 zval_ptr_dtor(orig_var);
2091 ZVAL_REF(orig_var, Z_REF_P(entry));
2092 } else {
2093 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2094 }
2095 count++;
2096 }
2097 }
2098 zval_ptr_dtor_str(&final_name);
2099 } else {
2100 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2101 continue;
2102 }
2103 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2104 goto prefix;
2105 }
2106 if (Z_ISREF_P(entry)) {
2107 Z_ADDREF_P(entry);
2108 } else {
2109 ZVAL_MAKE_REF_EX(entry, 2);
2110 }
2111 zend_hash_add_new(symbol_table, var_name, entry);
2112 count++;
2113 }
2114 } ZEND_HASH_FOREACH_END();
2115
2116 return count;
2117 }
2118 /* }}} */
2119
2120 static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2121 {
2122 zend_long count = 0;
2123 zend_string *var_name;
2124 zval *entry, *orig_var, final_name;
2125
2126 if (HT_IS_PACKED(arr)) {
2127 return 0;
2128 }
2129 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2130 if (!var_name) {
2131 continue;
2132 }
2133 if (ZSTR_LEN(var_name) == 0) {
2134 continue;
2135 }
2136 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2137 if (orig_var) {
2138 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2139 orig_var = Z_INDIRECT_P(orig_var);
2140 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2141 ZVAL_COPY_DEREF(orig_var, entry);
2142 count++;
2143 continue;
2144 }
2145 }
2146 prefix:
2147 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2148 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2149 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2150 zend_throw_error(NULL, "Cannot re-assign $this");
2151 return -1;
2152 } else {
2153 ZVAL_DEREF(entry);
2154 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2155 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2156 orig_var = Z_INDIRECT_P(orig_var);
2157 }
2158 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2159 if (UNEXPECTED(EG(exception))) {
2160 zend_string_release_ex(Z_STR(final_name), 0);
2161 return -1;
2162 }
2163 } else {
2164 Z_TRY_ADDREF_P(entry);
2165 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2166 }
2167 count++;
2168 }
2169 }
2170 zval_ptr_dtor_str(&final_name);
2171 } else {
2172 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2173 continue;
2174 }
2175 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2176 goto prefix;
2177 }
2178 ZVAL_DEREF(entry);
2179 Z_TRY_ADDREF_P(entry);
2180 zend_hash_add_new(symbol_table, var_name, entry);
2181 count++;
2182 }
2183 } ZEND_HASH_FOREACH_END();
2184
2185 return count;
2186 }
2187 /* }}} */
2188
2189 static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2190 {
2191 zend_long count = 0;
2192 zend_string *var_name;
2193 zend_ulong num_key;
2194 zval *entry, *orig_var, final_name;
2195
2196 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2197 if (var_name) {
2198 if (ZSTR_LEN(var_name) == 0) {
2199 continue;
2200 }
2201 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2202 } else {
2203 zend_string *str = zend_long_to_str(num_key);
2204 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2205 zend_string_release_ex(str, 0);
2206 }
2207 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2208 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2209 zend_throw_error(NULL, "Cannot re-assign $this");
2210 return -1;
2211 } else {
2212 if (Z_ISREF_P(entry)) {
2213 Z_ADDREF_P(entry);
2214 } else {
2215 ZVAL_MAKE_REF_EX(entry, 2);
2216 }
2217 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2218 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2219 orig_var = Z_INDIRECT_P(orig_var);
2220 }
2221 zval_ptr_dtor(orig_var);
2222 ZVAL_REF(orig_var, Z_REF_P(entry));
2223 } else {
2224 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2225 }
2226 count++;
2227 }
2228 }
2229 zval_ptr_dtor_str(&final_name);
2230 } ZEND_HASH_FOREACH_END();
2231
2232 return count;
2233 }
2234 /* }}} */
2235
2236 static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2237 {
2238 zend_long count = 0;
2239 zend_string *var_name;
2240 zend_ulong num_key;
2241 zval *entry, *orig_var, final_name;
2242
2243 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2244 if (var_name) {
2245 if (ZSTR_LEN(var_name) == 0) {
2246 continue;
2247 }
2248 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2249 } else {
2250 zend_string *str = zend_long_to_str(num_key);
2251 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2252 zend_string_release_ex(str, 0);
2253 }
2254 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2255 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2256 zend_throw_error(NULL, "Cannot re-assign $this");
2257 return -1;
2258 } else {
2259 ZVAL_DEREF(entry);
2260 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2261 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2262 orig_var = Z_INDIRECT_P(orig_var);
2263 }
2264 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2265 if (UNEXPECTED(EG(exception))) {
2266 zend_string_release_ex(Z_STR(final_name), 0);
2267 return -1;
2268 }
2269 } else {
2270 Z_TRY_ADDREF_P(entry);
2271 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2272 }
2273 count++;
2274 }
2275 }
2276 zval_ptr_dtor_str(&final_name);
2277 } ZEND_HASH_FOREACH_END();
2278
2279 return count;
2280 }
2281 /* }}} */
2282
2283 static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2284 {
2285 zend_long count = 0;
2286 zend_string *var_name;
2287 zend_ulong num_key;
2288 zval *entry, *orig_var, final_name;
2289
2290 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2291 if (var_name) {
2292 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2293 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2294 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2295 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2296 zval_ptr_dtor_str(&final_name);
2297 continue;
2298 }
2299 } else {
2300 ZVAL_STR_COPY(&final_name, var_name);
2301 }
2302 } else {
2303 zend_string *str = zend_long_to_str(num_key);
2304 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2305 zend_string_release_ex(str, 0);
2306 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2307 zval_ptr_dtor_str(&final_name);
2308 continue;
2309 }
2310 }
2311 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2312 zend_throw_error(NULL, "Cannot re-assign $this");
2313 return -1;
2314 } else {
2315 if (Z_ISREF_P(entry)) {
2316 Z_ADDREF_P(entry);
2317 } else {
2318 ZVAL_MAKE_REF_EX(entry, 2);
2319 }
2320 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2321 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2322 orig_var = Z_INDIRECT_P(orig_var);
2323 }
2324 zval_ptr_dtor(orig_var);
2325 ZVAL_REF(orig_var, Z_REF_P(entry));
2326 } else {
2327 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2328 }
2329 count++;
2330 }
2331 zval_ptr_dtor_str(&final_name);
2332 } ZEND_HASH_FOREACH_END();
2333
2334 return count;
2335 }
2336 /* }}} */
2337
2338 static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2339 {
2340 zend_long count = 0;
2341 zend_string *var_name;
2342 zend_ulong num_key;
2343 zval *entry, *orig_var, final_name;
2344
2345 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2346 if (var_name) {
2347 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2348 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2349 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2350 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2351 zval_ptr_dtor_str(&final_name);
2352 continue;
2353 }
2354 } else {
2355 ZVAL_STR_COPY(&final_name, var_name);
2356 }
2357 } else {
2358 zend_string *str = zend_long_to_str(num_key);
2359 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2360 zend_string_release_ex(str, 0);
2361 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2362 zval_ptr_dtor_str(&final_name);
2363 continue;
2364 }
2365 }
2366 if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2367 zend_throw_error(NULL, "Cannot re-assign $this");
2368 return -1;
2369 } else {
2370 ZVAL_DEREF(entry);
2371 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2372 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2373 orig_var = Z_INDIRECT_P(orig_var);
2374 }
2375 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2376 if (UNEXPECTED(EG(exception))) {
2377 zend_string_release_ex(Z_STR(final_name), 0);
2378 return -1;
2379 }
2380 } else {
2381 Z_TRY_ADDREF_P(entry);
2382 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2383 }
2384 count++;
2385 }
2386 zval_ptr_dtor_str(&final_name);
2387 } ZEND_HASH_FOREACH_END();
2388
2389 return count;
2390 }
2391 /* }}} */
2392
2393 static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2394 {
2395 zend_long count = 0;
2396 zend_string *var_name;
2397 zval *entry, *orig_var;
2398
2399 if (HT_IS_PACKED(arr)) {
2400 return 0;
2401 }
2402 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2403 if (!var_name) {
2404 continue;
2405 }
2406 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2407 continue;
2408 }
2409 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2410 continue;
2411 }
2412 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2413 if (orig_var) {
2414 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2415 orig_var = Z_INDIRECT_P(orig_var);
2416 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2417 if (Z_ISREF_P(entry)) {
2418 Z_ADDREF_P(entry);
2419 } else {
2420 ZVAL_MAKE_REF_EX(entry, 2);
2421 }
2422 ZVAL_REF(orig_var, Z_REF_P(entry));
2423 count++;
2424 }
2425 }
2426 } else {
2427 if (Z_ISREF_P(entry)) {
2428 Z_ADDREF_P(entry);
2429 } else {
2430 ZVAL_MAKE_REF_EX(entry, 2);
2431 }
2432 zend_hash_add_new(symbol_table, var_name, entry);
2433 count++;
2434 }
2435 } ZEND_HASH_FOREACH_END();
2436
2437 return count;
2438 }
2439 /* }}} */
2440
2441 static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2442 {
2443 zend_long count = 0;
2444 zend_string *var_name;
2445 zval *entry, *orig_var;
2446
2447 if (HT_IS_PACKED(arr)) {
2448 return 0;
2449 }
2450 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2451 if (!var_name) {
2452 continue;
2453 }
2454 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2455 continue;
2456 }
2457 if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2458 continue;
2459 }
2460 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2461 if (orig_var) {
2462 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2463 orig_var = Z_INDIRECT_P(orig_var);
2464 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2465 ZVAL_COPY_DEREF(orig_var, entry);
2466 count++;
2467 }
2468 }
2469 } else {
2470 ZVAL_DEREF(entry);
2471 Z_TRY_ADDREF_P(entry);
2472 zend_hash_add_new(symbol_table, var_name, entry);
2473 count++;
2474 }
2475 } ZEND_HASH_FOREACH_END();
2476
2477 return count;
2478 }
2479 /* }}} */
2480
2481 /* {{{ Imports variables into symbol table from an array */
2482 PHP_FUNCTION(extract)
2483 {
2484 zval *var_array_param;
2485 zend_long extract_refs;
2486 zend_long extract_type = PHP_EXTR_OVERWRITE;
2487 zend_string *prefix = NULL;
2488 zend_long count;
2489 zend_array *symbol_table;
2490
2491 ZEND_PARSE_PARAMETERS_START(1, 3)
2492 Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2493 Z_PARAM_OPTIONAL
2494 Z_PARAM_LONG(extract_type)
2495 Z_PARAM_STR(prefix)
2496 ZEND_PARSE_PARAMETERS_END();
2497
2498 extract_refs = (extract_type & PHP_EXTR_REFS);
2499 if (extract_refs) {
2500 SEPARATE_ARRAY(var_array_param);
2501 }
2502 extract_type &= 0xff;
2503
2504 if (extract_type < PHP_EXTR_OVERWRITE || extract_type > PHP_EXTR_IF_EXISTS) {
2505 zend_argument_value_error(2, "must be a valid extract type");
2506 RETURN_THROWS();
2507 }
2508
2509 if (extract_type > PHP_EXTR_SKIP && extract_type <= PHP_EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2510 zend_argument_value_error(3, "is required when using this extract type");
2511 RETURN_THROWS();
2512 }
2513
2514 if (prefix) {
2515 if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
2516 zend_argument_value_error(3, "must be a valid identifier");
2517 RETURN_THROWS();
2518 }
2519 }
2520
2521 if (zend_forbid_dynamic_call() == FAILURE) {
2522 return;
2523 }
2524
2525 symbol_table = zend_rebuild_symbol_table();
2526 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2527
2528 if (extract_refs) {
2529 switch (extract_type) {
2530 case PHP_EXTR_IF_EXISTS:
2531 count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2532 break;
2533 case PHP_EXTR_OVERWRITE:
2534 count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2535 break;
2536 case PHP_EXTR_PREFIX_IF_EXISTS:
2537 count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2538 break;
2539 case PHP_EXTR_PREFIX_SAME:
2540 count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2541 break;
2542 case PHP_EXTR_PREFIX_ALL:
2543 count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2544 break;
2545 case PHP_EXTR_PREFIX_INVALID:
2546 count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2547 break;
2548 default:
2549 count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2550 break;
2551 }
2552 } else {
2553 /* The array might be stored in a local variable that will be overwritten */
2554 zval array_copy;
2555 ZVAL_COPY(&array_copy, var_array_param);
2556 switch (extract_type) {
2557 case PHP_EXTR_IF_EXISTS:
2558 count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2559 break;
2560 case PHP_EXTR_OVERWRITE:
2561 count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2562 break;
2563 case PHP_EXTR_PREFIX_IF_EXISTS:
2564 count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2565 break;
2566 case PHP_EXTR_PREFIX_SAME:
2567 count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2568 break;
2569 case PHP_EXTR_PREFIX_ALL:
2570 count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2571 break;
2572 case PHP_EXTR_PREFIX_INVALID:
2573 count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2574 break;
2575 default:
2576 count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2577 break;
2578 }
2579 zval_ptr_dtor(&array_copy);
2580 }
2581
2582 RETURN_LONG(count);
2583 }
2584 /* }}} */
2585
2586 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2587 {
2588 zval *value_ptr, data;
2589
2590 ZVAL_DEREF(entry);
2591 if (Z_TYPE_P(entry) == IS_STRING) {
2592 if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2593 ZVAL_DEREF(value_ptr);
2594 Z_TRY_ADDREF_P(value_ptr);
2595 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2596 } else if (zend_string_equals(Z_STR_P(entry), ZSTR_KNOWN(ZEND_STR_THIS))) {
2597 zend_object *object = zend_get_this_object(EG(current_execute_data));
2598 if (object) {
2599 ZVAL_OBJ_COPY(&data, object);
2600 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2601 }
2602 } else {
2603 php_error_docref(NULL, E_WARNING, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
2604 }
2605 } else if (Z_TYPE_P(entry) == IS_ARRAY) {
2606 if (Z_REFCOUNTED_P(entry)) {
2607 if (Z_IS_RECURSIVE_P(entry)) {
2608 zend_throw_error(NULL, "Recursion detected");
2609 return;
2610 }
2611 Z_PROTECT_RECURSION_P(entry);
2612 }
2613 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
2614 php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
2615 } ZEND_HASH_FOREACH_END();
2616 if (Z_REFCOUNTED_P(entry)) {
2617 Z_UNPROTECT_RECURSION_P(entry);
2618 }
2619 } else {
2620 php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2621 return;
2622 }
2623 }
2624 /* }}} */
2625
2626 /* {{{ Creates a hash containing variables and their values */
2627 PHP_FUNCTION(compact)
2628 {
2629 zval *args = NULL; /* function arguments array */
2630 uint32_t num_args, i;
2631 zend_array *symbol_table;
2632
2633 ZEND_PARSE_PARAMETERS_START(1, -1)
2634 Z_PARAM_VARIADIC('+', args, num_args)
2635 ZEND_PARSE_PARAMETERS_END();
2636
2637 if (zend_forbid_dynamic_call() == FAILURE) {
2638 return;
2639 }
2640
2641 symbol_table = zend_rebuild_symbol_table();
2642 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2643
2644 /* compact() is probably most used with a single array of var_names
2645 or multiple string names, rather than a combination of both.
2646 So quickly guess a minimum result size based on that */
2647 if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2648 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2649 } else {
2650 array_init_size(return_value, num_args);
2651 }
2652
2653 for (i = 0; i < num_args; i++) {
2654 php_compact_var(symbol_table, return_value, &args[i], i + 1);
2655 }
2656 }
2657 /* }}} */
2658
2659 /* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2660 PHP_FUNCTION(array_fill)
2661 {
2662 zval *val;
2663 zend_long start_key, num;
2664
2665 ZEND_PARSE_PARAMETERS_START(3, 3)
2666 Z_PARAM_LONG(start_key)
2667 Z_PARAM_LONG(num)
2668 Z_PARAM_ZVAL(val)
2669 ZEND_PARSE_PARAMETERS_END();
2670
2671 if (EXPECTED(num > 0)) {
2672 if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2673 zend_argument_value_error(2, "is too large");
2674 RETURN_THROWS();
2675 } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2676 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2677 RETURN_THROWS();
2678 } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2679 /* create packed array */
2680 zval *zv;
2681
2682 array_init_size(return_value, (uint32_t)(start_key + num));
2683 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2684 Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2685 Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2686 Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2687
2688 if (Z_REFCOUNTED_P(val)) {
2689 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2690 }
2691
2692 zv = Z_ARRVAL_P(return_value)->arPacked;
2693
2694 while (start_key--) {
2695 ZVAL_UNDEF(zv);
2696 zv++;
2697 }
2698 while (num--) {
2699 ZVAL_COPY_VALUE(zv, val);
2700 zv++;
2701 }
2702 } else {
2703 /* create hash */
2704 array_init_size(return_value, (uint32_t)num);
2705 zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2706 if (Z_REFCOUNTED_P(val)) {
2707 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2708 }
2709 zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2710 while (--num) {
2711 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2712 start_key++;
2713 }
2714 }
2715 } else if (EXPECTED(num == 0)) {
2716 RETURN_EMPTY_ARRAY();
2717 } else {
2718 zend_argument_value_error(2, "must be greater than or equal to 0");
2719 RETURN_THROWS();
2720 }
2721 }
2722 /* }}} */
2723
2724 /* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2725 PHP_FUNCTION(array_fill_keys)
2726 {
2727 zval *keys, *val, *entry;
2728
2729 ZEND_PARSE_PARAMETERS_START(2, 2)
2730 Z_PARAM_ARRAY(keys)
2731 Z_PARAM_ZVAL(val)
2732 ZEND_PARSE_PARAMETERS_END();
2733
2734 /* Initialize return array */
2735 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2736
2737 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2738 ZVAL_DEREF(entry);
2739 Z_TRY_ADDREF_P(val);
2740 if (Z_TYPE_P(entry) == IS_LONG) {
2741 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2742 } else {
2743 zend_string *tmp_key;
2744 zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2745 zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2746 zend_tmp_string_release(tmp_key);
2747 }
2748 } ZEND_HASH_FOREACH_END();
2749 }
2750 /* }}} */
2751
2752 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2753 double __calc_size = ((start - end) / (_step)) + 1; \
2754 if (__calc_size >= (double)HT_MAX_SIZE) { \
2755 zend_value_error(\
2756 "The supplied range exceeds the maximum array size: start=%0.1f end=%0.1f step=%0.1f", end, start, (_step)); \
2757 RETURN_THROWS(); \
2758 } \
2759 size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2760 array_init_size(return_value, size); \
2761 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2762 } while (0)
2763
2764 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2765 zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2766 if (__calc_size >= HT_MAX_SIZE - 1) { \
2767 zend_value_error(\
2768 "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT " step=" ZEND_LONG_FMT, end, start, (_step)); \
2769 RETURN_THROWS(); \
2770 } \
2771 size = (uint32_t)(__calc_size + 1); \
2772 array_init_size(return_value, size); \
2773 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2774 } while (0)
2775
2776 /* Process input for the range() function
2777 * 0 on exceptions
2778 * IS_LONG if only interpretable as int
2779 * IS_DOUBLE if only interpretable as float
2780 * IS_STRING if only interpretable as string
2781 * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
2782 static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2783 {
2784 switch (Z_TYPE_P(input)) {
2785 case IS_LONG:
2786 *lval = Z_LVAL_P(input);
2787 *dval = (double) Z_LVAL_P(input);
2788 return IS_LONG;
2789 case IS_DOUBLE:
2790 *dval = Z_DVAL_P(input);
2791 check_dval_value:
2792 if (zend_isinf(*dval)) {
2793 zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2794 return 0;
2795 }
2796 if (zend_isnan(*dval)) {
2797 zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2798 return 0;
2799 }
2800 return IS_DOUBLE;
2801 case IS_STRING: {
2802 /* Process strings:
2803 * - Empty strings are converted to 0 with a diagnostic
2804 * - Check if string is numeric and store the values in passed pointer
2805 * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2806 * - If numeric int, check it is one byte or not
2807 * - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2808 * - If not should only be interpreted as int, return IS_LONG;
2809 * - Otherwise is a string and return IS_STRING */
2810 if (Z_STRLEN_P(input) == 0) {
2811 const char *arg_name = get_active_function_arg_name(arg_num);
2812 php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2813 if (UNEXPECTED(EG(exception))) {
2814 return 0;
2815 }
2816 *lval = 0;
2817 *dval = 0.0;
2818 return IS_LONG;
2819 }
2820 uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2821 if (type == IS_DOUBLE) {
2822 goto check_dval_value;
2823 }
2824 if (type == IS_LONG) {
2825 *dval = (double) *lval;
2826 if (Z_STRLEN_P(input) == 1) {
2827 return IS_ARRAY;
2828 } else {
2829 return IS_LONG;
2830 }
2831 }
2832 if (Z_STRLEN_P(input) != 1) {
2833 const char *arg_name = get_active_function_arg_name(arg_num);
2834 php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2835 if (UNEXPECTED(EG(exception))) {
2836 return 0;
2837 }
2838 }
2839 /* Set fall back values to 0 in case the other argument is not a string */
2840 *lval = 0;
2841 *dval = 0.0;
2842 return IS_STRING;
2843 }
2844 EMPTY_SWITCH_DEFAULT_CASE();
2845 }
2846 }
2847
2848 /* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2849 PHP_FUNCTION(range)
2850 {
2851 zval *user_start, *user_end, *user_step = NULL, tmp;
2852 bool is_step_double = false;
2853 bool is_step_negative = false;
2854 double step_double = 1.0;
2855 zend_long step = 1;
2856
2857 ZEND_PARSE_PARAMETERS_START(2, 3)
2858 Z_PARAM_NUMBER_OR_STR(user_start)
2859 Z_PARAM_NUMBER_OR_STR(user_end)
2860 Z_PARAM_OPTIONAL
2861 Z_PARAM_NUMBER(user_step)
2862 ZEND_PARSE_PARAMETERS_END();
2863
2864 if (user_step) {
2865 if (UNEXPECTED(Z_TYPE_P(user_step) == IS_DOUBLE)) {
2866 step_double = Z_DVAL_P(user_step);
2867
2868 if (zend_isinf(step_double)) {
2869 zend_argument_value_error(3, "must be a finite number, INF provided");
2870 RETURN_THROWS();
2871 }
2872 if (zend_isnan(step_double)) {
2873 zend_argument_value_error(3, "must be a finite number, NAN provided");
2874 RETURN_THROWS();
2875 }
2876
2877 /* We only want positive step values. */
2878 if (step_double < 0.0) {
2879 is_step_negative = true;
2880 step_double *= -1;
2881 }
2882 step = zend_dval_to_lval(step_double);
2883 if (!zend_is_long_compatible(step_double, step)) {
2884 is_step_double = true;
2885 }
2886 } else {
2887 step = Z_LVAL_P(user_step);
2888 /* We only want positive step values. */
2889 if (step < 0) {
2890 if (UNEXPECTED(step == ZEND_LONG_MIN)) {
2891 zend_argument_value_error(3, "must be greater than " ZEND_LONG_FMT, step);
2892 RETURN_THROWS();
2893 }
2894 is_step_negative = true;
2895 step *= -1;
2896 }
2897 step_double = (double) step;
2898 }
2899 if (step_double == 0.0) {
2900 zend_argument_value_error(3, "cannot be 0");
2901 RETURN_THROWS();
2902 }
2903 }
2904
2905 uint8_t start_type;
2906 double start_double;
2907 zend_long start_long;
2908 uint8_t end_type;
2909 double end_double;
2910 zend_long end_long;
2911
2912 start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2913 if (start_type == 0) {
2914 RETURN_THROWS();
2915 }
2916 end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2917 if (end_type == 0) {
2918 RETURN_THROWS();
2919 }
2920
2921 /* If the range is given as strings, generate an array of characters. */
2922 if (start_type >= IS_STRING || end_type >= IS_STRING) {
2923 /* If one of the inputs is NOT a string nor single-byte string */
2924 if (UNEXPECTED(start_type < IS_STRING || end_type < IS_STRING)) {
2925 if (start_type < IS_STRING) {
2926 if (end_type != IS_ARRAY) {
2927 php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if"
2928 " argument #2 ($end) is a single byte string, argument #2 ($end) converted to 0");
2929 }
2930 end_type = IS_LONG;
2931 } else if (end_type < IS_STRING) {
2932 if (start_type != IS_ARRAY) {
2933 php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a single byte string if"
2934 " argument #1 ($start) is a single byte string, argument #1 ($start) converted to 0");
2935 }
2936 start_type = IS_LONG;
2937 }
2938 if (UNEXPECTED(EG(exception))) {
2939 RETURN_THROWS();
2940 }
2941 goto handle_numeric_inputs;
2942 }
2943
2944 if (is_step_double) {
2945 /* Only emit warning if one of the input is not a numeric digit */
2946 if (start_type == IS_STRING || end_type == IS_STRING) {
2947 php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2948 " of characters, inputs converted to 0");
2949 }
2950 if (UNEXPECTED(EG(exception))) {
2951 RETURN_THROWS();
2952 }
2953 end_type = IS_LONG;
2954 start_type = IS_LONG;
2955 goto handle_numeric_inputs;
2956 }
2957
2958 /* Generate array of characters */
2959 unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
2960 unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
2961
2962 /* Decreasing char range */
2963 if (low > high) {
2964 if (low - high < step) {
2965 goto boundary_error;
2966 }
2967 /* Initialize the return_value as an array. */
2968 array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
2969 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2970 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2971 for (; low >= high; low -= (unsigned int)step) {
2972 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2973 ZEND_HASH_FILL_NEXT();
2974 if (((signed int)low - step) < 0) {
2975 break;
2976 }
2977 }
2978 } ZEND_HASH_FILL_END();
2979 } else if (high > low) { /* Increasing char range */
2980 if (is_step_negative) {
2981 goto negative_step_error;
2982 }
2983 if (high - low < step) {
2984 goto boundary_error;
2985 }
2986 array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
2987 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2988 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2989 for (; low <= high; low += (unsigned int)step) {
2990 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2991 ZEND_HASH_FILL_NEXT();
2992 if (((signed int)low + step) > 255) {
2993 break;
2994 }
2995 }
2996 } ZEND_HASH_FILL_END();
2997 } else {
2998 array_init(return_value);
2999 ZVAL_CHAR(&tmp, low);
3000 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3001 }
3002 return;
3003 }
3004
3005 handle_numeric_inputs:
3006 if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
3007 double element;
3008 uint32_t i, size;
3009
3010 /* Decreasing float range */
3011 if (start_double > end_double) {
3012 if (start_double - end_double < step_double) {
3013 goto boundary_error;
3014 }
3015
3016 RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
3017
3018 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3019 for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
3020 ZEND_HASH_FILL_SET_DOUBLE(element);
3021 ZEND_HASH_FILL_NEXT();
3022 }
3023 } ZEND_HASH_FILL_END();
3024 } else if (end_double > start_double) { /* Increasing float range */
3025 if (is_step_negative) {
3026 goto negative_step_error;
3027 }
3028 if (end_double - start_double < step_double) {
3029 goto boundary_error;
3030 }
3031
3032 RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
3033
3034 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3035 for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
3036 ZEND_HASH_FILL_SET_DOUBLE(element);
3037 ZEND_HASH_FILL_NEXT();
3038 }
3039 } ZEND_HASH_FILL_END();
3040 } else {
3041 array_init(return_value);
3042 ZVAL_DOUBLE(&tmp, start_double);
3043 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3044 }
3045 } else {
3046 ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
3047 /* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
3048 zend_ulong unsigned_step= (zend_ulong)step;
3049 uint32_t i, size;
3050
3051 /* Decreasing int range */
3052 if (start_long > end_long) {
3053 if ((zend_ulong)start_long - end_long < unsigned_step) {
3054 goto boundary_error;
3055 }
3056
3057 RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3058
3059 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3060 for (i = 0; i < size; ++i) {
3061 ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3062 ZEND_HASH_FILL_NEXT();
3063 }
3064 } ZEND_HASH_FILL_END();
3065 } else if (end_long > start_long) { /* Increasing int range */
3066 if (is_step_negative) {
3067 goto negative_step_error;
3068 }
3069 if ((zend_ulong)end_long - start_long < unsigned_step) {
3070 goto boundary_error;
3071 }
3072
3073 RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3074
3075 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3076 for (i = 0; i < size; ++i) {
3077 ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3078 ZEND_HASH_FILL_NEXT();
3079 }
3080 } ZEND_HASH_FILL_END();
3081 } else {
3082 array_init(return_value);
3083 ZVAL_LONG(&tmp, start_long);
3084 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3085 }
3086 }
3087 return;
3088
3089 negative_step_error:
3090 zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3091 RETURN_THROWS();
3092
3093 boundary_error:
3094 zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3095 RETURN_THROWS();
3096 }
3097 /* }}} */
3098
3099 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
3100 #undef RANGE_CHECK_LONG_INIT_ARRAY
3101
3102 /* {{{ php_array_data_shuffle */
3103 PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array) /* {{{ */
3104 {
3105 int64_t idx, j, n_elems, rnd_idx, n_left;
3106 zval *zv, temp;
3107 HashTable *hash;
3108
3109 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3110
3111 if (n_elems < 1) {
3112 return true;
3113 }
3114
3115 hash = Z_ARRVAL_P(array);
3116 n_left = n_elems;
3117
3118 if (!HT_IS_PACKED(hash)) {
3119 if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3120 Bucket *p = hash->arData;
3121 zend_long i = hash->nNumUsed;
3122
3123 for (; i > 0; p++, i--) {
3124 if (p->key) {
3125 zend_string_release(p->key);
3126 p->key = NULL;
3127 }
3128 }
3129 }
3130 zend_hash_to_packed(hash);
3131 }
3132
3133 if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3134 if (hash->nNumUsed != hash->nNumOfElements) {
3135 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3136 zv = hash->arPacked + idx;
3137 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3138 if (j != idx) {
3139 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3140 }
3141 j++;
3142 }
3143 }
3144 while (--n_left) {
3145 rnd_idx = algo->range(status, 0, n_left);
3146 if (EG(exception)) {
3147 return false;
3148 }
3149 if (rnd_idx != n_left) {
3150 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3151 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3152 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3153 }
3154 }
3155 } else {
3156 zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3157
3158 if (hash->nNumUsed != hash->nNumOfElements) {
3159 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3160 zv = hash->arPacked + idx;
3161 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3162 if (j != idx) {
3163 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3164 if (idx == iter_pos) {
3165 zend_hash_iterators_update(hash, idx, j);
3166 iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3167 }
3168 }
3169 j++;
3170 }
3171 }
3172 while (--n_left) {
3173 rnd_idx = algo->range(status, 0, n_left);
3174 if (EG(exception)) {
3175 return false;
3176 }
3177 if (rnd_idx != n_left) {
3178 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3179 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3180 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3181 zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
3182 }
3183 }
3184 }
3185 hash->nNumUsed = n_elems;
3186 hash->nInternalPointer = 0;
3187 hash->nNextFreeElement = n_elems;
3188
3189 return true;
3190 }
3191 /* }}} */
3192
3193 /* {{{ Randomly shuffle the contents of an array */
3194 PHP_FUNCTION(shuffle)
3195 {
3196 zval *array;
3197
3198 ZEND_PARSE_PARAMETERS_START(1, 1)
3199 Z_PARAM_ARRAY_EX(array, 0, 1)
3200 ZEND_PARSE_PARAMETERS_END();
3201
3202 php_array_data_shuffle(php_random_default_algo(), php_random_default_status(), array);
3203
3204 RETURN_TRUE;
3205 }
3206 /* }}} */
3207
3208 static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
3209 {
3210 HashTable out_hash; /* Output hashtable */
3211 zend_long num_in; /* Number of entries in the input hashtable */
3212 zend_long pos; /* Current position in the hashtable */
3213 uint32_t idx;
3214 zval *entry; /* Hash entry */
3215 uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3216
3217 /* Get number of entries in the input hash */
3218 num_in = zend_hash_num_elements(in_hash);
3219
3220 /* Clamp the offset.. */
3221 if (offset > num_in) {
3222 offset = num_in;
3223 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3224 offset = 0;
3225 }
3226
3227 /* ..and the length */
3228 if (length < 0) {
3229 length = num_in - offset + length;
3230 } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3231 length = num_in - offset;
3232 }
3233
3234 /* Create and initialize output hash */
3235 zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
3236
3237 if (HT_IS_PACKED(in_hash)) {
3238 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3239 entry = in_hash->arPacked;
3240 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3241 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3242
3243 zend_hash_next_index_insert_new(&out_hash, entry);
3244 if (idx == iter_pos) {
3245 if ((zend_long)idx != pos) {
3246 zend_hash_iterators_update(in_hash, idx, pos);
3247 }
3248 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3249 }
3250 pos++;
3251 }
3252
3253 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3254 if (removed != NULL) {
3255 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3256 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3257 pos++;
3258 Z_TRY_ADDREF_P(entry);
3259 zend_hash_next_index_insert_new(removed, entry);
3260 zend_hash_packed_del_val(in_hash, entry);
3261 }
3262 } else { /* otherwise just skip those entries */
3263 int pos2 = pos;
3264
3265 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3266 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3267 pos2++;
3268 zend_hash_packed_del_val(in_hash, entry);
3269 }
3270 }
3271 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3272
3273 /* If there are entries to insert.. */
3274 if (replace) {
3275 ZEND_HASH_FOREACH_VAL(replace, entry) {
3276 Z_TRY_ADDREF_P(entry);
3277 zend_hash_next_index_insert_new(&out_hash, entry);
3278 pos++;
3279 } ZEND_HASH_FOREACH_END();
3280 }
3281
3282 /* Copy the remaining input hash entries to the output hash */
3283 entry = in_hash->arPacked + idx;
3284 for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3285 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3286 zend_hash_next_index_insert_new(&out_hash, entry);
3287 if (idx == iter_pos) {
3288 if ((zend_long)idx != pos) {
3289 zend_hash_iterators_update(in_hash, idx, pos);
3290 }
3291 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3292 }
3293 pos++;
3294 }
3295 } else {
3296 Bucket *p = in_hash->arData;
3297
3298 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3299 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, p++) {
3300 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3301 entry = &p->val;
3302
3303 /* Update output hash depending on key type */
3304 if (p->key == NULL) {
3305 zend_hash_next_index_insert_new(&out_hash, entry);
3306 } else {
3307 zend_hash_add_new(&out_hash, p->key, entry);
3308 }
3309 if (idx == iter_pos) {
3310 if ((zend_long)idx != pos) {
3311 zend_hash_iterators_update(in_hash, idx, pos);
3312 }
3313 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3314 }
3315 pos++;
3316 }
3317
3318 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3319 if (removed != NULL) {
3320 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++, p++) {
3321 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3322 pos++;
3323 entry = &p->val;
3324 Z_TRY_ADDREF_P(entry);
3325 if (p->key == NULL) {
3326 zend_hash_next_index_insert_new(removed, entry);
3327 } else {
3328 zend_hash_add_new(removed, p->key, entry);
3329 }
3330 zend_hash_del_bucket(in_hash, p);
3331 }
3332 } else { /* otherwise just skip those entries */
3333 int pos2 = pos;
3334
3335 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++, p++) {
3336 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3337 pos2++;
3338 zend_hash_del_bucket(in_hash, p);
3339 }
3340 }
3341 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3342
3343 /* If there are entries to insert.. */
3344 if (replace) {
3345 ZEND_HASH_FOREACH_VAL(replace, entry) {
3346 Z_TRY_ADDREF_P(entry);
3347 zend_hash_next_index_insert_new(&out_hash, entry);
3348 pos++;
3349 } ZEND_HASH_FOREACH_END();
3350 }
3351
3352 /* Copy the remaining input hash entries to the output hash */
3353 for ( ; idx < in_hash->nNumUsed ; idx++, p++) {
3354 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3355 entry = &p->val;
3356 if (p->key == NULL) {
3357 zend_hash_next_index_insert_new(&out_hash, entry);
3358 } else {
3359 zend_hash_add_new(&out_hash, p->key, entry);
3360 }
3361 if (idx == iter_pos) {
3362 if ((zend_long)idx != pos) {
3363 zend_hash_iterators_update(in_hash, idx, pos);
3364 }
3365 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3366 }
3367 pos++;
3368 }
3369 }
3370
3371 /* replace HashTable data */
3372 HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3373 HT_SET_ITERATORS_COUNT(in_hash, 0);
3374 in_hash->pDestructor = NULL;
3375 zend_hash_destroy(in_hash);
3376
3377 HT_FLAGS(in_hash) = HT_FLAGS(&out_hash);
3378 in_hash->nTableSize = out_hash.nTableSize;
3379 in_hash->nTableMask = out_hash.nTableMask;
3380 in_hash->nNumUsed = out_hash.nNumUsed;
3381 in_hash->nNumOfElements = out_hash.nNumOfElements;
3382 in_hash->nNextFreeElement = out_hash.nNextFreeElement;
3383 in_hash->arData = out_hash.arData;
3384 in_hash->pDestructor = out_hash.pDestructor;
3385
3386 zend_hash_internal_pointer_reset(in_hash);
3387 }
3388 /* }}} */
3389
3390 /* {{{ Pushes elements onto the end of the array */
3391 PHP_FUNCTION(array_push)
3392 {
3393 zval *args, /* Function arguments array */
3394 *stack, /* Input array */
3395 new_var; /* Variable to be pushed */
3396 uint32_t argc; /* Number of function arguments */
3397
3398
3399 ZEND_PARSE_PARAMETERS_START(1, -1)
3400 Z_PARAM_ARRAY_EX(stack, 0, 1)
3401 Z_PARAM_VARIADIC('+', args, argc)
3402 ZEND_PARSE_PARAMETERS_END();
3403
3404 /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3405 for (uint32_t i = 0; i < argc; i++) {
3406 ZVAL_COPY(&new_var, &args[i]);
3407
3408 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
3409 Z_TRY_DELREF(new_var);
3410 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3411 RETURN_THROWS();
3412 }
3413 }
3414
3415 /* Clean up and return the number of values in the stack */
3416 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3417 }
3418 /* }}} */
3419
3420 /* {{{ Pops an element off the end of the array */
3421 PHP_FUNCTION(array_pop)
3422 {
3423 zval *stack, /* Input stack */
3424 *val; /* Value to be popped */
3425 uint32_t idx;
3426
3427 ZEND_PARSE_PARAMETERS_START(1, 1)
3428 Z_PARAM_ARRAY_EX(stack, 0, 1)
3429 ZEND_PARSE_PARAMETERS_END();
3430
3431 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3432 return;
3433 }
3434
3435 if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3436 /* Get the last value and copy it into the return value */
3437 idx = Z_ARRVAL_P(stack)->nNumUsed;
3438 while (1) {
3439 if (idx == 0) {
3440 return;
3441 }
3442 idx--;
3443 val = Z_ARRVAL_P(stack)->arPacked + idx;
3444 if (Z_TYPE_P(val) != IS_UNDEF) {
3445 break;
3446 }
3447 }
3448 RETVAL_COPY_DEREF(val);
3449
3450 if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3451 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3452 }
3453
3454 /* Delete the last value */
3455 zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3456 } else {
3457 Bucket *p;
3458
3459 /* Get the last value and copy it into the return value */
3460 idx = Z_ARRVAL_P(stack)->nNumUsed;
3461 while (1) {
3462 if (idx == 0) {
3463 return;
3464 }
3465 idx--;
3466 p = Z_ARRVAL_P(stack)->arData + idx;
3467 val = &p->val;
3468 if (Z_TYPE_P(val) != IS_UNDEF) {
3469 break;
3470 }
3471 }
3472 RETVAL_COPY_DEREF(val);
3473
3474 if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3475 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3476 }
3477
3478 /* Delete the last value */
3479 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3480 }
3481 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3482 }
3483 /* }}} */
3484
3485 /* {{{ Pops an element off the beginning of the array */
3486 PHP_FUNCTION(array_shift)
3487 {
3488 zval *stack, /* Input stack */
3489 *val; /* Value to be popped */
3490 uint32_t idx;
3491
3492 ZEND_PARSE_PARAMETERS_START(1, 1)
3493 Z_PARAM_ARRAY_EX(stack, 0, 1)
3494 ZEND_PARSE_PARAMETERS_END();
3495
3496 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3497 return;
3498 }
3499
3500 /* re-index like it did before */
3501 if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3502 uint32_t k = 0;
3503
3504 /* Get the first value and copy it into the return value */
3505 idx = 0;
3506 while (1) {
3507 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3508 return;
3509 }
3510 val = Z_ARRVAL_P(stack)->arPacked + idx;
3511 if (Z_TYPE_P(val) != IS_UNDEF) {
3512 break;
3513 }
3514 idx++;
3515 }
3516 RETVAL_COPY_DEREF(val);
3517
3518 /* Delete the first value */
3519 zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3520
3521 if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3522 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3523 val = Z_ARRVAL_P(stack)->arPacked + idx;
3524 if (Z_TYPE_P(val) == IS_UNDEF) continue;
3525 if (idx != k) {
3526 zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3527 ZVAL_COPY_VALUE(q, val);
3528 ZVAL_UNDEF(val);
3529 }
3530 k++;
3531 }
3532 } else {
3533 uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3534
3535 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3536 val = Z_ARRVAL_P(stack)->arPacked + idx;
3537 if (Z_TYPE_P(val) == IS_UNDEF) continue;
3538 if (idx != k) {
3539 zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3540 ZVAL_COPY_VALUE(q, val);
3541 ZVAL_UNDEF(val);
3542 if (idx == iter_pos) {
3543 zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3544 iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3545 }
3546 }
3547 k++;
3548 }
3549 }
3550 Z_ARRVAL_P(stack)->nNumUsed = k;
3551 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3552 } else {
3553 uint32_t k = 0;
3554 int should_rehash = 0;
3555 Bucket *p;
3556
3557 /* Get the first value and copy it into the return value */
3558 idx = 0;
3559 while (1) {
3560 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3561 return;
3562 }
3563 p = Z_ARRVAL_P(stack)->arData + idx;
3564 val = &p->val;
3565 if (Z_TYPE_P(val) != IS_UNDEF) {
3566 break;
3567 }
3568 idx++;
3569 }
3570 RETVAL_COPY_DEREF(val);
3571
3572 /* Delete the first value */
3573 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3574
3575 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3576 p = Z_ARRVAL_P(stack)->arData + idx;
3577 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3578 if (p->key == NULL) {
3579 if (p->h != k) {
3580 p->h = k++;
3581 should_rehash = 1;
3582 } else {
3583 k++;
3584 }
3585 }
3586 }
3587 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3588 if (should_rehash) {
3589 zend_hash_rehash(Z_ARRVAL_P(stack));
3590 }
3591 }
3592
3593 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3594 }
3595 /* }}} */
3596
3597 /* {{{ Pushes elements onto the beginning of the array */
3598 PHP_FUNCTION(array_unshift)
3599 {
3600 zval *args, /* Function arguments array */
3601 *stack; /* Input stack */
3602 HashTable new_hash; /* New hashtable for the stack */
3603 uint32_t argc; /* Number of function arguments */
3604 zend_string *key;
3605 zval *value;
3606
3607 ZEND_PARSE_PARAMETERS_START(1, -1)
3608 Z_PARAM_ARRAY_EX(stack, 0, 1)
3609 Z_PARAM_VARIADIC('+', args, argc)
3610 ZEND_PARSE_PARAMETERS_END();
3611
3612 zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3613 for (uint32_t i = 0; i < argc; i++) {
3614 Z_TRY_ADDREF(args[i]);
3615 zend_hash_next_index_insert_new(&new_hash, &args[i]);
3616 }
3617
3618 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3619 if (key) {
3620 zend_hash_add_new(&new_hash, key, value);
3621 } else {
3622 zend_hash_next_index_insert_new(&new_hash, value);
3623 }
3624 } ZEND_HASH_FOREACH_END();
3625
3626 if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3627 zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3628 HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3629 HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3630 }
3631
3632 /* replace HashTable data */
3633 Z_ARRVAL_P(stack)->pDestructor = NULL;
3634 zend_hash_destroy(Z_ARRVAL_P(stack));
3635
3636 HT_FLAGS(Z_ARRVAL_P(stack)) = HT_FLAGS(&new_hash);
3637 Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
3638 Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
3639 Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
3640 Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
3641 Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
3642 Z_ARRVAL_P(stack)->arData = new_hash.arData;
3643 Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
3644
3645 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3646
3647 /* Clean up and return the number of elements in the stack */
3648 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3649 }
3650 /* }}} */
3651
3652 /* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3653 PHP_FUNCTION(array_splice)
3654 {
3655 zval *array, /* Input array */
3656 *repl_array = NULL; /* Replacement array */
3657 HashTable *rem_hash = NULL;
3658 zend_long offset,
3659 length = 0;
3660 bool length_is_null = 1;
3661 int num_in; /* Number of elements in the input array */
3662
3663 ZEND_PARSE_PARAMETERS_START(2, 4)
3664 Z_PARAM_ARRAY_EX(array, 0, 1)
3665 Z_PARAM_LONG(offset)
3666 Z_PARAM_OPTIONAL
3667 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3668 Z_PARAM_ZVAL(repl_array)
3669 ZEND_PARSE_PARAMETERS_END();
3670
3671 num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3672
3673 if (length_is_null) {
3674 length = num_in;
3675 }
3676
3677 if (ZEND_NUM_ARGS() == 4) {
3678 /* Make sure the last argument, if passed, is an array */
3679 convert_to_array(repl_array);
3680 }
3681
3682 /* Don't create the array of removed elements if it's not going
3683 * to be used; e.g. only removing and/or replacing elements */
3684 if (USED_RET()) {
3685 zend_long size = length;
3686
3687 /* Clamp the offset.. */
3688 if (offset > num_in) {
3689 offset = num_in;
3690 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3691 offset = 0;
3692 }
3693
3694 /* ..and the length */
3695 if (length < 0) {
3696 size = num_in - offset + length;
3697 } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
3698 size = num_in - offset;
3699 }
3700
3701 /* Initialize return value */
3702 array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3703 rem_hash = Z_ARRVAL_P(return_value);
3704 } else {
3705 /* The return value will not be used, but make sure it still has the correct type. */
3706 RETVAL_EMPTY_ARRAY();
3707 }
3708
3709 /* Perform splice */
3710 php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3711 }
3712 /* }}} */
3713
3714 /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3715 Finds the bucket at the given valid offset */
3716 static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3717 {
3718 zend_long pos;
3719 Bucket *bucket;
3720 ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3721 if (HT_IS_WITHOUT_HOLES(ht)) {
3722 /* There's no need to iterate over the array to filter out holes if there are no holes */
3723 /* This properly handles both packed and unpacked arrays. */
3724 return ht->arData + offset;
3725 }
3726 /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3727 pos = 0;
3728 ZEND_HASH_MAP_FOREACH_BUCKET(ht, bucket) {
3729 if (pos >= offset) {
3730 /* This is the bucket of the array element at the requested offset */
3731 return bucket;
3732 }
3733 ++pos;
3734 } ZEND_HASH_FOREACH_END();
3735
3736 /* Return a pointer to the end of the bucket array. */
3737 return ht->arData + ht->nNumUsed;
3738 }
3739 /* }}} */
3740
3741 /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3742 Finds the bucket at the given valid offset */
3743 static inline zval* find_packed_val_at_offset(HashTable* ht, zend_long offset)
3744 {
3745 zend_long pos;
3746 zval *zv;
3747 ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3748 if (HT_IS_WITHOUT_HOLES(ht)) {
3749 /* There's no need to iterate over the array to filter out holes if there are no holes */
3750 /* This properly handles both packed and unpacked arrays. */
3751 return ht->arPacked + offset;
3752 }
3753 /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3754 pos = 0;
3755 ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) {
3756 if (pos >= offset) {
3757 /* This is the bucket of the array element at the requested offset */
3758 return zv;
3759 }
3760 ++pos;
3761 } ZEND_HASH_FOREACH_END();
3762
3763 /* Return a pointer to the end of the bucket array. */
3764 return ht->arPacked + ht->nNumUsed;
3765 }
3766 /* }}} */
3767
3768 /* {{{ Returns elements specified by offset and length */
3769 PHP_FUNCTION(array_slice)
3770 {
3771 zval *input; /* Input array */
3772 zval *entry; /* An array entry */
3773 zend_long offset; /* Offset to get elements from */
3774 zend_long length = 0; /* How many elements to get */
3775 bool length_is_null = 1; /* Whether an explicit length has been omitted */
3776 bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array */
3777 uint32_t num_in; /* Number of elements in the input array */
3778 zend_string *string_key;
3779 zend_ulong num_key;
3780
3781 ZEND_PARSE_PARAMETERS_START(2, 4)
3782 Z_PARAM_ARRAY(input)
3783 Z_PARAM_LONG(offset)
3784 Z_PARAM_OPTIONAL
3785 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3786 Z_PARAM_BOOL(preserve_keys)
3787 ZEND_PARSE_PARAMETERS_END();
3788
3789 /* Get number of entries in the input hash */
3790 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
3791
3792 /* We want all entries from offset to the end if length is not passed or is null */
3793 if (length_is_null) {
3794 length = num_in;
3795 }
3796
3797 /* Clamp the offset.. */
3798 if (offset > (zend_long) num_in) {
3799 RETURN_EMPTY_ARRAY();
3800 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3801 offset = 0;
3802 }
3803
3804 /* ..and the length */
3805 if (length < 0) {
3806 length = num_in - offset + length;
3807 } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3808 length = num_in - offset;
3809 }
3810
3811 if (length <= 0) {
3812 RETURN_EMPTY_ARRAY();
3813 }
3814
3815 /* Initialize returned array */
3816 array_init_size(return_value, (uint32_t)length);
3817
3818 // Contains modified variants of ZEND_HASH_FOREACH_VAL
3819 {
3820 HashTable *ht = Z_ARRVAL_P(input);
3821
3822 /* Start at the beginning and go until we hit offset */
3823 if (HT_IS_PACKED(ht)) {
3824 zval *zv = find_packed_val_at_offset(ht, offset);
3825 zval *end = ht->arPacked + ht->nNumUsed;
3826
3827 if (!preserve_keys
3828 || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3829 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3830 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3831 for (; zv != end; zv++) {
3832 if (__fill_idx >= length) {
3833 break;
3834 }
3835 if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3836 continue;
3837 }
3838 entry = zv;
3839 if (UNEXPECTED(Z_ISREF_P(entry)) &&
3840 UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3841 entry = Z_REFVAL_P(entry);
3842 }
3843 Z_TRY_ADDREF_P(entry);
3844 ZEND_HASH_FILL_ADD(entry);
3845 }
3846 } ZEND_HASH_FILL_END();
3847 } else {
3848 zend_long n = 0; /* Current number of elements */
3849 zend_long idx = zv - ht->arPacked;
3850
3851 for (; zv != end; zv++, idx++) {
3852 if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3853 continue;
3854 }
3855 if (n >= length) {
3856 break;
3857 }
3858 n++;
3859 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, zv);
3860 zval_add_ref(entry);
3861 }
3862 }
3863 } else {
3864 zend_long n = 0; /* Current number of elements */
3865 Bucket *p = find_bucket_at_offset(ht, offset);
3866 Bucket *end = ht->arData + ht->nNumUsed;
3867
3868 for (; p != end; p++) {
3869 entry = &p->val;
3870 if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3871 continue;
3872 }
3873 if (n >= length) {
3874 break;
3875 }
3876 n++;
3877 num_key = p->h;
3878 string_key = p->key;
3879
3880 if (string_key) {
3881 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3882 } else {
3883 if (preserve_keys) {
3884 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3885 } else {
3886 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3887 }
3888 }
3889 zval_add_ref(entry);
3890 }
3891 }
3892 }
3893 }
3894 /* }}} */
3895
3896 PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
3897 {
3898 zval *src_entry, *dest_entry;
3899 zend_string *string_key;
3900
3901 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3902 if (string_key) {
3903 if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
3904 zval *src_zval = src_entry;
3905 zval *dest_zval = dest_entry;
3906 HashTable *thash;
3907 zval tmp;
3908 int ret;
3909
3910 ZVAL_DEREF(src_zval);
3911 ZVAL_DEREF(dest_zval);
3912 thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
3913 if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
3914 zend_throw_error(NULL, "Recursion detected");
3915 return 0;
3916 }
3917
3918 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
3919 dest_zval = dest_entry;
3920
3921 if (Z_TYPE_P(dest_zval) == IS_NULL) {
3922 convert_to_array(dest_zval);
3923 add_next_index_null(dest_zval);
3924 } else {
3925 convert_to_array(dest_zval);
3926 }
3927 SEPARATE_ZVAL(dest_zval);
3928
3929 ZVAL_UNDEF(&tmp);
3930 if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3931 ZVAL_COPY(&tmp, src_zval);
3932 convert_to_array(&tmp);
3933 src_zval = &tmp;
3934 }
3935 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3936 if (thash) {
3937 GC_TRY_PROTECT_RECURSION(thash);
3938 }
3939 ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3940 if (thash) {
3941 GC_TRY_UNPROTECT_RECURSION(thash);
3942 }
3943 if (!ret) {
3944 return 0;
3945 }
3946 } else {
3947 Z_TRY_ADDREF_P(src_zval);
3948 zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3949 if (EXPECTED(!zv)) {
3950 Z_TRY_DELREF_P(src_zval);
3951 zend_cannot_add_element();
3952 return 0;
3953 }
3954 }
3955 zval_ptr_dtor(&tmp);
3956 } else {
3957 zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3958 zval_add_ref(zv);
3959 }
3960 } else {
3961 zval *zv = zend_hash_next_index_insert(dest, src_entry);
3962 if (UNEXPECTED(!zv)) {
3963 zend_cannot_add_element();
3964 return 0;
3965 }
3966 zval_add_ref(zv);
3967 }
3968 } ZEND_HASH_FOREACH_END();
3969 return 1;
3970 }
3971 /* }}} */
3972
3973 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3974 {
3975 zval *src_entry;
3976 zend_string *string_key;
3977
3978 if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
3979 zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
3980 ZEND_HASH_FILL_PACKED(dest) {
3981 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3982 if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
3983 UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
3984 src_entry = Z_REFVAL_P(src_entry);
3985 }
3986 Z_TRY_ADDREF_P(src_entry);
3987 ZEND_HASH_FILL_ADD(src_entry);
3988 } ZEND_HASH_FOREACH_END();
3989 } ZEND_HASH_FILL_END();
3990 } else {
3991 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3992 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3993 Z_REFCOUNT_P(src_entry) == 1)) {
3994 src_entry = Z_REFVAL_P(src_entry);
3995 }
3996 Z_TRY_ADDREF_P(src_entry);
3997 if (string_key) {
3998 zend_hash_update(dest, string_key, src_entry);
3999 } else {
4000 zend_hash_next_index_insert_new(dest, src_entry);
4001 }
4002 } ZEND_HASH_FOREACH_END();
4003 }
4004 return 1;
4005 }
4006 /* }}} */
4007
4008 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4009 {
4010 zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4011 zend_string *string_key;
4012 zend_ulong num_key;
4013 int ret;
4014
4015 ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4016 src_zval = src_entry;
4017 ZVAL_DEREF(src_zval);
4018 if (string_key) {
4019 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4020 (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4021 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4022 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4023
4024 zval *zv = zend_hash_update(dest, string_key, src_entry);
4025 zval_add_ref(zv);
4026 continue;
4027 }
4028 } else {
4029 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4030 (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4031 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4032 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4033
4034 zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4035 zval_add_ref(zv);
4036 continue;
4037 }
4038 }
4039
4040 dest_zval = dest_entry;
4041 ZVAL_DEREF(dest_zval);
4042 if (Z_IS_RECURSIVE_P(dest_zval) ||
4043 Z_IS_RECURSIVE_P(src_zval) ||
4044 (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
4045 zend_throw_error(NULL, "Recursion detected");
4046 return 0;
4047 }
4048
4049 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4050 SEPARATE_ZVAL(dest_entry);
4051 dest_zval = dest_entry;
4052
4053 if (Z_REFCOUNTED_P(dest_zval)) {
4054 Z_PROTECT_RECURSION_P(dest_zval);
4055 }
4056 if (Z_REFCOUNTED_P(src_zval)) {
4057 Z_PROTECT_RECURSION_P(src_zval);
4058 }
4059
4060 ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4061
4062 if (Z_REFCOUNTED_P(dest_zval)) {
4063 Z_UNPROTECT_RECURSION_P(dest_zval);
4064 }
4065 if (Z_REFCOUNTED_P(src_zval)) {
4066 Z_UNPROTECT_RECURSION_P(src_zval);
4067 }
4068
4069 if (!ret) {
4070 return 0;
4071 }
4072 } ZEND_HASH_FOREACH_END();
4073
4074 return 1;
4075 }
4076 /* }}} */
4077
4078 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4079 {
4080 zval *args = NULL;
4081 zval *arg;
4082 uint32_t argc, i;
4083 HashTable *dest;
4084
4085 ZEND_PARSE_PARAMETERS_START(1, -1)
4086 Z_PARAM_VARIADIC('+', args, argc)
4087 ZEND_PARSE_PARAMETERS_END();
4088
4089
4090 for (i = 0; i < argc; i++) {
4091 zval *arg = args + i;
4092
4093 if (Z_TYPE_P(arg) != IS_ARRAY) {
4094 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4095 RETURN_THROWS();
4096 }
4097 }
4098
4099 /* copy first array if necessary */
4100 arg = args;
4101 bool in_place = zend_may_modify_arg_in_place(arg);
4102 if (in_place) {
4103 dest = Z_ARRVAL_P(arg);
4104 } else {
4105 dest = zend_array_dup(Z_ARRVAL_P(arg));
4106 }
4107
4108 ZVAL_ARR(return_value, dest);
4109
4110 if (recursive) {
4111 for (i = 1; i < argc; i++) {
4112 arg = args + i;
4113 php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4114 }
4115 } else {
4116 for (i = 1; i < argc; i++) {
4117 arg = args + i;
4118 zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4119 }
4120 }
4121
4122 if (in_place) {
4123 GC_ADDREF(dest);
4124 }
4125 }
4126 /* }}} */
4127
4128 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4129 {
4130 zval *args = NULL;
4131 zval *arg;
4132 uint32_t argc, i;
4133 zval *src_entry;
4134 HashTable *src, *dest;
4135 uint32_t count = 0;
4136
4137 ZEND_PARSE_PARAMETERS_START(0, -1)
4138 Z_PARAM_VARIADIC('+', args, argc)
4139 ZEND_PARSE_PARAMETERS_END();
4140
4141 if (argc == 0) {
4142 RETURN_EMPTY_ARRAY();
4143 }
4144
4145 for (i = 0; i < argc; i++) {
4146 zval *arg = args + i;
4147
4148 if (Z_TYPE_P(arg) != IS_ARRAY) {
4149 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4150 RETURN_THROWS();
4151 }
4152 count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4153 }
4154
4155 if (argc == 2) {
4156 zval *ret = NULL;
4157
4158 if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4159 ret = &args[1];
4160 } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4161 ret = &args[0];
4162 }
4163 if (ret) {
4164 if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4165 if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4166 ZVAL_COPY(return_value, ret);
4167 return;
4168 }
4169 } else {
4170 bool copy = 1;
4171 zend_string *string_key;
4172
4173 ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4174 if (!string_key) {
4175 copy = 0;
4176 break;
4177 }
4178 } ZEND_HASH_FOREACH_END();
4179 if (copy) {
4180 ZVAL_COPY(return_value, ret);
4181 return;
4182 }
4183 }
4184 }
4185 }
4186
4187 arg = args;
4188 src = Z_ARRVAL_P(arg);
4189 /* copy first array if necessary */
4190 bool in_place = false;
4191 if (HT_IS_PACKED(src)) {
4192 /* Note: If it has holes, it might get sequentialized */
4193 if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4194 dest = src;
4195 in_place = true;
4196 ZVAL_ARR(return_value, dest);
4197 } else {
4198 array_init_size(return_value, count);
4199 dest = Z_ARRVAL_P(return_value);
4200
4201 zend_hash_real_init_packed(dest);
4202 ZEND_HASH_FILL_PACKED(dest) {
4203 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4204 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4205 Z_REFCOUNT_P(src_entry) == 1)) {
4206 src_entry = Z_REFVAL_P(src_entry);
4207 }
4208 Z_TRY_ADDREF_P(src_entry);
4209 ZEND_HASH_FILL_ADD(src_entry);
4210 } ZEND_HASH_FOREACH_END();
4211 } ZEND_HASH_FILL_END();
4212 }
4213 } else {
4214 array_init_size(return_value, count);
4215 dest = Z_ARRVAL_P(return_value);
4216
4217 zend_string *string_key;
4218 zend_hash_real_init_mixed(dest);
4219 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4220 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4221 Z_REFCOUNT_P(src_entry) == 1)) {
4222 src_entry = Z_REFVAL_P(src_entry);
4223 }
4224 Z_TRY_ADDREF_P(src_entry);
4225 if (EXPECTED(string_key)) {
4226 _zend_hash_append(dest, string_key, src_entry);
4227 } else {
4228 zend_hash_next_index_insert_new(dest, src_entry);
4229 }
4230 } ZEND_HASH_FOREACH_END();
4231 }
4232 if (recursive) {
4233 for (i = 1; i < argc; i++) {
4234 arg = args + i;
4235 php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4236 }
4237 } else {
4238 for (i = 1; i < argc; i++) {
4239 arg = args + i;
4240 php_array_merge(dest, Z_ARRVAL_P(arg));
4241 }
4242 }
4243
4244 if (in_place) {
4245 GC_ADDREF(dest);
4246 }
4247 }
4248 /* }}} */
4249
4250 /* {{{ Merges elements from passed arrays into one array */
4251 PHP_FUNCTION(array_merge)
4252 {
4253 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4254 }
4255 /* }}} */
4256
4257 /* {{{ Recursively merges elements from passed arrays into one array */
4258 PHP_FUNCTION(array_merge_recursive)
4259 {
4260 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4261 }
4262 /* }}} */
4263
4264 /* {{{ Replaces elements from passed arrays into one array */
4265 PHP_FUNCTION(array_replace)
4266 {
4267 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4268 }
4269 /* }}} */
4270
4271 /* {{{ Recursively replaces elements from passed arrays into one array */
4272 PHP_FUNCTION(array_replace_recursive)
4273 {
4274 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4275 }
4276 /* }}} */
4277
4278 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4279 PHP_FUNCTION(array_keys)
4280 {
4281 zval *input, /* Input array */
4282 *search_value = NULL, /* Value to search for */
4283 *entry, /* An entry in the input array */
4284 new_val; /* New value */
4285 bool strict = 0; /* do strict comparison */
4286 zend_ulong num_idx;
4287 zend_string *str_idx;
4288 zend_array *arrval;
4289 zend_ulong elem_count;
4290
4291 ZEND_PARSE_PARAMETERS_START(1, 3)
4292 Z_PARAM_ARRAY(input)
4293 Z_PARAM_OPTIONAL
4294 Z_PARAM_ZVAL(search_value)
4295 Z_PARAM_BOOL(strict)
4296 ZEND_PARSE_PARAMETERS_END();
4297 arrval = Z_ARRVAL_P(input);
4298 elem_count = zend_hash_num_elements(arrval);
4299
4300 /* Base case: empty input */
4301 if (!elem_count) {
4302 RETURN_COPY(input);
4303 }
4304
4305 /* Initialize return array */
4306 if (search_value != NULL) {
4307 array_init(return_value);
4308
4309 if (strict) {
4310 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4311 ZVAL_DEREF(entry);
4312 if (fast_is_identical_function(search_value, entry)) {
4313 if (str_idx) {
4314 ZVAL_STR_COPY(&new_val, str_idx);
4315 } else {
4316 ZVAL_LONG(&new_val, num_idx);
4317 }
4318 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4319 }
4320 } ZEND_HASH_FOREACH_END();
4321 } else {
4322 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4323 if (fast_equal_check_function(search_value, entry)) {
4324 if (str_idx) {
4325 ZVAL_STR_COPY(&new_val, str_idx);
4326 } else {
4327 ZVAL_LONG(&new_val, num_idx);
4328 }
4329 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4330 }
4331 } ZEND_HASH_FOREACH_END();
4332 }
4333 } else {
4334 array_init_size(return_value, elem_count);
4335 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4336 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4337 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4338 /* Optimistic case: range(0..n-1) for vector-like packed array */
4339 zend_ulong lval = 0;
4340
4341 for (; lval < elem_count; ++lval) {
4342 ZEND_HASH_FILL_SET_LONG(lval);
4343 ZEND_HASH_FILL_NEXT();
4344 }
4345 } else {
4346 /* Go through input array and add keys to the return array */
4347 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4348 if (str_idx) {
4349 ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4350 } else {
4351 ZEND_HASH_FILL_SET_LONG(num_idx);
4352 }
4353 ZEND_HASH_FILL_NEXT();
4354 } ZEND_HASH_FOREACH_END();
4355 }
4356 } ZEND_HASH_FILL_END();
4357 }
4358 }
4359 /* }}} */
4360
4361 /* {{{ Get the key of the first element of the array */
4362 PHP_FUNCTION(array_key_first)
4363 {
4364 zval *stack; /* Input stack */
4365
4366 ZEND_PARSE_PARAMETERS_START(1, 1)
4367 Z_PARAM_ARRAY(stack)
4368 ZEND_PARSE_PARAMETERS_END();
4369
4370 HashTable *target_hash = Z_ARRVAL_P (stack);
4371 HashPosition pos = 0;
4372 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4373 }
4374 /* }}} */
4375
4376 /* {{{ Get the key of the last element of the array */
4377 PHP_FUNCTION(array_key_last)
4378 {
4379 zval *stack; /* Input stack */
4380 HashPosition pos;
4381
4382 ZEND_PARSE_PARAMETERS_START(1, 1)
4383 Z_PARAM_ARRAY(stack)
4384 ZEND_PARSE_PARAMETERS_END();
4385
4386 HashTable *target_hash = Z_ARRVAL_P (stack);
4387 zend_hash_internal_pointer_end_ex(target_hash, &pos);
4388 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4389 }
4390 /* }}} */
4391
4392 /* {{{ Return just the values from the input array */
4393 PHP_FUNCTION(array_values)
4394 {
4395 zval *input; /* Input array */
4396 zend_array *arrval;
4397 zend_long arrlen;
4398
4399 ZEND_PARSE_PARAMETERS_START(1, 1)
4400 Z_PARAM_ARRAY(input)
4401 ZEND_PARSE_PARAMETERS_END();
4402
4403 arrval = Z_ARRVAL_P(input);
4404
4405 /* Return empty input as is */
4406 arrlen = zend_hash_num_elements(arrval);
4407 if (!arrlen) {
4408 RETURN_EMPTY_ARRAY();
4409 }
4410
4411 /* Return vector-like packed arrays as-is */
4412 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4413 arrval->nNextFreeElement == arrlen) {
4414 RETURN_COPY(input);
4415 }
4416
4417 RETURN_ARR(zend_array_to_list(arrval));
4418 }
4419 /* }}} */
4420
4421 /* {{{ Return the value as key and the frequency of that value in input as value */
4422 PHP_FUNCTION(array_count_values)
4423 {
4424 zval *input, /* Input array */
4425 *entry, /* An entry in the input array */
4426 *tmp;
4427 HashTable *myht;
4428
4429 ZEND_PARSE_PARAMETERS_START(1, 1)
4430 Z_PARAM_ARRAY(input)
4431 ZEND_PARSE_PARAMETERS_END();
4432
4433 /* Initialize return array */
4434 array_init(return_value);
4435
4436 /* Go through input array and add values to the return array */
4437 myht = Z_ARRVAL_P(input);
4438 ZEND_HASH_FOREACH_VAL(myht, entry) {
4439 ZVAL_DEREF(entry);
4440 if (Z_TYPE_P(entry) == IS_LONG) {
4441 if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4442 zval data;
4443 ZVAL_LONG(&data, 1);
4444 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4445 } else {
4446 Z_LVAL_P(tmp)++;
4447 }
4448 } else if (Z_TYPE_P(entry) == IS_STRING) {
4449 if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4450 zval data;
4451 ZVAL_LONG(&data, 1);
4452 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4453 } else {
4454 Z_LVAL_P(tmp)++;
4455 }
4456 } else {
4457 php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4458 }
4459 } ZEND_HASH_FOREACH_END();
4460 }
4461 /* }}} */
4462
4463 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4464 {
4465 zval *prop = NULL;
4466
4467 if (Z_TYPE_P(data) == IS_OBJECT) {
4468 zend_string *tmp_str;
4469 /* If name is an integer convert integer to string */
4470 if (name_str == NULL) {
4471 tmp_str = zend_long_to_str(name_long);
4472 } else {
4473 tmp_str = zend_string_copy(name_str);
4474 }
4475 /* The has_property check is first performed in "exists" mode (which returns true for
4476 * properties that are null but exist) and then in "has" mode to handle objects that
4477 * implement __isset (which is not called in "exists" mode). */
4478 if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4479 || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4480 prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4481 if (prop) {
4482 ZVAL_DEREF(prop);
4483 if (prop != rv) {
4484 Z_TRY_ADDREF_P(prop);
4485 }
4486 }
4487 }
4488 zend_string_release(tmp_str);
4489 } else if (Z_TYPE_P(data) == IS_ARRAY) {
4490 /* Name is a string */
4491 if (name_str != NULL) {
4492 prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4493 } else {
4494 prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4495 }
4496 if (prop) {
4497 ZVAL_DEREF(prop);
4498 Z_TRY_ADDREF_P(prop);
4499 }
4500 }
4501
4502 return prop;
4503 }
4504 /* }}} */
4505
4506 /* {{{ Return the values from a single column in the input array, identified by the
4507 value_key and optionally indexed by the index_key */
4508 PHP_FUNCTION(array_column)
4509 {
4510 HashTable *input;
4511 zval *colval, *data, rv;
4512 zend_string *column_str = NULL;
4513 zend_long column_long = 0;
4514 bool column_is_null = 0;
4515 zend_string *index_str = NULL;
4516 zend_long index_long = 0;
4517 bool index_is_null = 1;
4518
4519 ZEND_PARSE_PARAMETERS_START(2, 3)
4520 Z_PARAM_ARRAY_HT(input)
4521 Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4522 Z_PARAM_OPTIONAL
4523 Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4524 ZEND_PARSE_PARAMETERS_END();
4525
4526 void* cache_slot_column[3] = { NULL, NULL, NULL };
4527 void* cache_slot_index[3] = { NULL, NULL, NULL };
4528
4529 array_init_size(return_value, zend_hash_num_elements(input));
4530 /* Index param is not passed */
4531 if (index_is_null) {
4532 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4533 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4534 ZEND_HASH_FOREACH_VAL(input, data) {
4535 ZVAL_DEREF(data);
4536 if (column_is_null) {
4537 Z_TRY_ADDREF_P(data);
4538 colval = data;
4539 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4540 continue;
4541 }
4542 ZEND_HASH_FILL_ADD(colval);
4543 } ZEND_HASH_FOREACH_END();
4544 } ZEND_HASH_FILL_END();
4545 } else {
4546 ZEND_HASH_FOREACH_VAL(input, data) {
4547 ZVAL_DEREF(data);
4548
4549 if (column_is_null) {
4550 Z_TRY_ADDREF_P(data);
4551 colval = data;
4552 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4553 continue;
4554 }
4555
4556 zval rv;
4557 zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4558 if (keyval) {
4559 array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4560 zval_ptr_dtor(colval);
4561 zval_ptr_dtor(keyval);
4562 } else {
4563 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4564 }
4565 } ZEND_HASH_FOREACH_END();
4566 }
4567 }
4568 /* }}} */
4569
4570 /* {{{ Return input as a new array with the order of the entries reversed */
4571 PHP_FUNCTION(array_reverse)
4572 {
4573 zval *input, /* Input array */
4574 *entry; /* An entry in the input array */
4575 zend_string *string_key;
4576 zend_ulong num_key;
4577 bool preserve_keys = 0; /* whether to preserve keys */
4578
4579 ZEND_PARSE_PARAMETERS_START(1, 2)
4580 Z_PARAM_ARRAY(input)
4581 Z_PARAM_OPTIONAL
4582 Z_PARAM_BOOL(preserve_keys)
4583 ZEND_PARSE_PARAMETERS_END();
4584
4585 /* Initialize return array */
4586 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4587 if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4588 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4589 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4590 ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4591 if (UNEXPECTED(Z_ISREF_P(entry) &&
4592 Z_REFCOUNT_P(entry) == 1)) {
4593 entry = Z_REFVAL_P(entry);
4594 }
4595 Z_TRY_ADDREF_P(entry);
4596 ZEND_HASH_FILL_ADD(entry);
4597 } ZEND_HASH_FOREACH_END();
4598 } ZEND_HASH_FILL_END();
4599 } else {
4600 ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4601 if (string_key) {
4602 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4603 } else {
4604 if (preserve_keys) {
4605 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4606 } else {
4607 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4608 }
4609 }
4610 zval_add_ref(entry);
4611 } ZEND_HASH_FOREACH_END();
4612 }
4613 }
4614 /* }}} */
4615
4616 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4617 PHP_FUNCTION(array_pad)
4618 {
4619 zval *input; /* Input array */
4620 zval *pad_value; /* Padding value obviously */
4621 zend_long pad_size; /* Size to pad to */
4622 zend_long pad_size_abs; /* Absolute value of pad_size */
4623 zend_long input_size; /* Size of the input array */
4624 zend_long num_pads; /* How many pads do we need */
4625 zend_long i;
4626 zend_string *key;
4627 zval *value;
4628
4629 ZEND_PARSE_PARAMETERS_START(3, 3)
4630 Z_PARAM_ARRAY(input)
4631 Z_PARAM_LONG(pad_size)
4632 Z_PARAM_ZVAL(pad_value)
4633 ZEND_PARSE_PARAMETERS_END();
4634
4635 if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4636 zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4637 RETURN_THROWS();
4638 }
4639
4640 /* Do some initial calculations */
4641 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4642 pad_size_abs = ZEND_ABS(pad_size);
4643
4644 if (input_size >= pad_size_abs) {
4645 /* Copy the original array */
4646 ZVAL_COPY(return_value, input);
4647 return;
4648 }
4649
4650 num_pads = pad_size_abs - input_size;
4651 if (Z_REFCOUNTED_P(pad_value)) {
4652 GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4653 }
4654
4655 array_init_size(return_value, pad_size_abs);
4656 if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4657 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4658
4659 if (pad_size < 0) {
4660 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4661 for (i = 0; i < num_pads; i++) {
4662 ZEND_HASH_FILL_ADD(pad_value);
4663 }
4664 } ZEND_HASH_FILL_END();
4665 }
4666
4667 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4668 ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4669 Z_TRY_ADDREF_P(value);
4670 ZEND_HASH_FILL_ADD(value);
4671 } ZEND_HASH_FOREACH_END();
4672 } ZEND_HASH_FILL_END();
4673
4674 if (pad_size > 0) {
4675 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4676 for (i = 0; i < num_pads; i++) {
4677 ZEND_HASH_FILL_ADD(pad_value);
4678 }
4679 } ZEND_HASH_FILL_END();
4680 }
4681 } else {
4682 if (pad_size < 0) {
4683 for (i = 0; i < num_pads; i++) {
4684 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4685 }
4686 }
4687
4688 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4689 Z_TRY_ADDREF_P(value);
4690 if (key) {
4691 zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4692 } else {
4693 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4694 }
4695 } ZEND_HASH_FOREACH_END();
4696
4697 if (pad_size > 0) {
4698 for (i = 0; i < num_pads; i++) {
4699 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4700 }
4701 }
4702 }
4703 }
4704 /* }}} */
4705
4706 /* {{{ Return array with key <-> value flipped */
4707 PHP_FUNCTION(array_flip)
4708 {
4709 zval *array, *entry, data;
4710 zend_ulong num_idx;
4711 zend_string *str_idx;
4712
4713 ZEND_PARSE_PARAMETERS_START(1, 1)
4714 Z_PARAM_ARRAY(array)
4715 ZEND_PARSE_PARAMETERS_END();
4716
4717 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4718
4719 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4720 ZVAL_DEREF(entry);
4721 if (Z_TYPE_P(entry) == IS_LONG) {
4722 if (str_idx) {
4723 ZVAL_STR_COPY(&data, str_idx);
4724 } else {
4725 ZVAL_LONG(&data, num_idx);
4726 }
4727 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4728 } else if (Z_TYPE_P(entry) == IS_STRING) {
4729 if (str_idx) {
4730 ZVAL_STR_COPY(&data, str_idx);
4731 } else {
4732 ZVAL_LONG(&data, num_idx);
4733 }
4734 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4735 } else {
4736 php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4737 }
4738 } ZEND_HASH_FOREACH_END();
4739 }
4740 /* }}} */
4741
4742 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4743 PHP_FUNCTION(array_change_key_case)
4744 {
4745 zval *array, *entry;
4746 zend_string *string_key;
4747 zend_string *new_key;
4748 zend_ulong num_key;
4749 zend_long change_to_upper=0;
4750
4751 ZEND_PARSE_PARAMETERS_START(1, 2)
4752 Z_PARAM_ARRAY(array)
4753 Z_PARAM_OPTIONAL
4754 Z_PARAM_LONG(change_to_upper)
4755 ZEND_PARSE_PARAMETERS_END();
4756
4757 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4758
4759 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4760 if (!string_key) {
4761 entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4762 } else {
4763 if (change_to_upper) {
4764 new_key = zend_string_toupper(string_key);
4765 } else {
4766 new_key = zend_string_tolower(string_key);
4767 }
4768 entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4769 zend_string_release_ex(new_key, 0);
4770 }
4771
4772 zval_add_ref(entry);
4773 } ZEND_HASH_FOREACH_END();
4774 }
4775 /* }}} */
4776
4777 struct bucketindex {
4778 Bucket b;
4779 unsigned int i;
4780 };
4781
4782 static void array_bucketindex_swap(void *p, void *q)
4783 {
4784 struct bucketindex *f = (struct bucketindex *)p;
4785 struct bucketindex *g = (struct bucketindex *)q;
4786 struct bucketindex t;
4787 t = *f;
4788 *f = *g;
4789 *g = t;
4790 }
4791
4792 /* {{{ Removes duplicate values from array */
4793 PHP_FUNCTION(array_unique)
4794 {
4795 zval *array;
4796 Bucket *p;
4797 zend_long sort_type = PHP_SORT_STRING;
4798 bucket_compare_func_t cmp;
4799 struct bucketindex *arTmp, *cmpdata, *lastkept;
4800 uint32_t i, idx;
4801
4802 ZEND_PARSE_PARAMETERS_START(1, 2)
4803 Z_PARAM_ARRAY(array)
4804 Z_PARAM_OPTIONAL
4805 Z_PARAM_LONG(sort_type)
4806 ZEND_PARSE_PARAMETERS_END();
4807
4808 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4809 ZVAL_COPY(return_value, array);
4810 return;
4811 }
4812
4813 if (sort_type == PHP_SORT_STRING) {
4814 HashTable seen;
4815 zend_long num_key;
4816 zend_string *str_key;
4817 zval *val;
4818
4819 zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4820 array_init(return_value);
4821
4822 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4823 zval *retval;
4824 if (Z_TYPE_P(val) == IS_STRING) {
4825 retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4826 } else {
4827 zend_string *tmp_str_val;
4828 zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4829 retval = zend_hash_add_empty_element(&seen, str_val);
4830 zend_tmp_string_release(tmp_str_val);
4831 }
4832
4833 if (retval) {
4834 /* First occurrence of the value */
4835 if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4836 ZVAL_DEREF(val);
4837 }
4838 Z_TRY_ADDREF_P(val);
4839
4840 if (str_key) {
4841 zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4842 } else {
4843 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4844 }
4845 }
4846 } ZEND_HASH_FOREACH_END();
4847
4848 zend_hash_destroy(&seen);
4849 return;
4850 }
4851
4852 cmp = php_get_data_compare_func_unstable(sort_type, 0);
4853
4854 bool in_place = zend_may_modify_arg_in_place(array);
4855 if (in_place) {
4856 RETVAL_ARR(Z_ARRVAL_P(array));
4857 } else {
4858 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4859 }
4860
4861 /* create and sort array with pointers to the target_hash buckets */
4862 arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4863 if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4864 zval *zv = Z_ARRVAL_P(array)->arPacked;
4865 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4866 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4867 ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4868 arTmp[i].b.h = idx;
4869 arTmp[i].b.key = NULL;
4870 arTmp[i].i = i;
4871 i++;
4872 }
4873 } else {
4874 p = Z_ARRVAL_P(array)->arData;
4875 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4876 if (Z_TYPE(p->val) == IS_UNDEF) continue;
4877 arTmp[i].b = *p;
4878 arTmp[i].i = i;
4879 i++;
4880 }
4881 }
4882 ZVAL_UNDEF(&arTmp[i].b.val);
4883 zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4884 (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4885 /* go through the sorted array and delete duplicates from the copy */
4886 lastkept = arTmp;
4887 for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4888 if (cmp(&lastkept->b, &cmpdata->b)) {
4889 lastkept = cmpdata;
4890 } else {
4891 if (lastkept->i > cmpdata->i) {
4892 p = &lastkept->b;
4893 lastkept = cmpdata;
4894 } else {
4895 p = &cmpdata->b;
4896 }
4897 if (p->key == NULL) {
4898 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4899 } else {
4900 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4901 }
4902 }
4903 }
4904 pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4905
4906 if (in_place) {
4907 Z_ADDREF_P(return_value);
4908 }
4909 }
4910 /* }}} */
4911
4912 static int zval_compare(zval *first, zval *second) /* {{{ */
4913 {
4914 return string_compare_function(first, second);
4915 }
4916 /* }}} */
4917
4918 static int zval_user_compare(zval *a, zval *b) /* {{{ */
4919 {
4920 zval args[2];
4921 zval retval;
4922
4923 ZVAL_COPY_VALUE(&args[0], a);
4924 ZVAL_COPY_VALUE(&args[1], b);
4925
4926 BG(user_compare_fci).param_count = 2;
4927 BG(user_compare_fci).params = args;
4928 BG(user_compare_fci).retval = &retval;
4929
4930 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4931 zend_long ret = zval_get_long(&retval);
4932 zval_ptr_dtor(&retval);
4933 return ZEND_NORMALIZE_BOOL(ret);
4934 } else {
4935 return 0;
4936 }
4937 }
4938 /* }}} */
4939
4940 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4941 {
4942 uint32_t argc, i;
4943 zval *args;
4944 int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4945 bool ok;
4946 zval *val, *data;
4947 char *param_spec;
4948 zend_string *key;
4949 zend_ulong h;
4950
4951 /* Get the argument count */
4952 argc = ZEND_NUM_ARGS();
4953 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4954 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
4955 param_spec = "+f";
4956 intersect_data_compare_func = zval_user_compare;
4957 } else {
4958 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
4959 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
4960 param_spec = "+";
4961
4962 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4963 intersect_data_compare_func = zval_compare;
4964 }
4965 }
4966
4967 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4968 RETURN_THROWS();
4969 }
4970
4971 for (i = 0; i < argc; i++) {
4972 if (Z_TYPE(args[i]) != IS_ARRAY) {
4973 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
4974 RETURN_THROWS();
4975 }
4976 }
4977
4978 array_init(return_value);
4979
4980 /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
4981 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4982 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4983 val = Z_REFVAL_P(val);
4984 }
4985 if (key == NULL) {
4986 ok = 1;
4987 for (i = 1; i < argc; i++) {
4988 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
4989 (intersect_data_compare_func &&
4990 intersect_data_compare_func(val, data) != 0)
4991 ) {
4992 ok = 0;
4993 break;
4994 }
4995 }
4996 if (ok) {
4997 Z_TRY_ADDREF_P(val);
4998 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4999 }
5000 } else {
5001 ok = 1;
5002 for (i = 1; i < argc; i++) {
5003 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5004 (intersect_data_compare_func &&
5005 intersect_data_compare_func(val, data) != 0)
5006 ) {
5007 ok = 0;
5008 break;
5009 }
5010 }
5011 if (ok) {
5012 Z_TRY_ADDREF_P(val);
5013 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5014 }
5015 }
5016 } ZEND_HASH_FOREACH_END();
5017 }
5018 /* }}} */
5019
5020 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5021 {
5022 zval *args = NULL;
5023 HashTable *hash;
5024 uint32_t arr_argc, i;
5025 int c = 0;
5026 uint32_t idx;
5027 Bucket **lists, *list, **ptrs, *p;
5028 char *param_spec;
5029 zend_fcall_info fci1, fci2;
5030 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5031 zend_fcall_info *fci_key = NULL, *fci_data;
5032 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5033 PHP_ARRAY_CMP_FUNC_VARS;
5034 bool in_place = false;
5035
5036 bucket_compare_func_t intersect_key_compare_func;
5037 bucket_compare_func_t intersect_data_compare_func;
5038
5039 if (behavior == INTERSECT_NORMAL) {
5040 intersect_key_compare_func = php_array_key_compare_string;
5041
5042 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5043 /* array_intersect() */
5044 param_spec = "+";
5045 intersect_data_compare_func = php_array_data_compare_string_unstable;
5046 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5047 /* array_uintersect() */
5048 param_spec = "+f";
5049 intersect_data_compare_func = php_array_user_compare_unstable;
5050 } else {
5051 ZEND_ASSERT(0 && "Invalid data_compare_type");
5052 return;
5053 }
5054
5055 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5056 RETURN_THROWS();
5057 }
5058 fci_data = &fci1;
5059 fci_data_cache = &fci1_cache;
5060
5061 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5062 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5063 * no comparison of the data is done (part of INTERSECT_ASSOC) */
5064
5065 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5066 /* array_intersect_assoc() or array_intersect_key() */
5067 param_spec = "+";
5068 intersect_key_compare_func = php_array_key_compare_string_unstable;
5069 intersect_data_compare_func = php_array_data_compare_string_unstable;
5070 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5071 /* array_uintersect_assoc() */
5072 param_spec = "+f";
5073 intersect_key_compare_func = php_array_key_compare_string_unstable;
5074 intersect_data_compare_func = php_array_user_compare_unstable;
5075 fci_data = &fci1;
5076 fci_data_cache = &fci1_cache;
5077 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5078 /* array_intersect_uassoc() or array_intersect_ukey() */
5079 param_spec = "+f";
5080 intersect_key_compare_func = php_array_user_key_compare_unstable;
5081 intersect_data_compare_func = php_array_data_compare_string_unstable;
5082 fci_key = &fci1;
5083 fci_key_cache = &fci1_cache;
5084 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5085 /* array_uintersect_uassoc() */
5086 param_spec = "+ff";
5087 intersect_key_compare_func = php_array_user_key_compare_unstable;
5088 intersect_data_compare_func = php_array_user_compare_unstable;
5089 fci_data = &fci1;
5090 fci_data_cache = &fci1_cache;
5091 fci_key = &fci2;
5092 fci_key_cache = &fci2_cache;
5093 } else {
5094 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5095 return;
5096 }
5097
5098 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5099 RETURN_THROWS();
5100 }
5101
5102 } else {
5103 ZEND_ASSERT(0 && "Invalid behavior");
5104 return;
5105 }
5106
5107 PHP_ARRAY_CMP_FUNC_BACKUP();
5108
5109 /* for each argument, create and sort list with pointers to the hash buckets */
5110 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5111 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5112
5113 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5114 BG(user_compare_fci) = *fci_data;
5115 BG(user_compare_fci_cache) = *fci_data_cache;
5116 } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5117 BG(user_compare_fci) = *fci_key;
5118 BG(user_compare_fci_cache) = *fci_key_cache;
5119 }
5120
5121 for (i = 0; i < arr_argc; i++) {
5122 if (Z_TYPE(args[i]) != IS_ARRAY) {
5123 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5124 arr_argc = i; /* only free up to i - 1 */
5125 goto out;
5126 }
5127 hash = Z_ARRVAL(args[i]);
5128 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5129 lists[i] = list;
5130 ptrs[i] = list;
5131 if (HT_IS_PACKED(hash)) {
5132 zval *zv = hash->arPacked;
5133 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5134 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5135 ZVAL_COPY_VALUE(&list->val, zv);
5136 list->h = idx;
5137 list->key = NULL;
5138 list++;
5139 }
5140 } else {
5141 p = hash->arData;
5142 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5143 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5144 *list++ = *p;
5145 }
5146 }
5147 ZVAL_UNDEF(&list->val);
5148 if (hash->nNumOfElements > 1) {
5149 if (behavior == INTERSECT_NORMAL) {
5150 zend_sort((void *) lists[i], hash->nNumOfElements,
5151 sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5152 (swap_func_t)zend_hash_bucket_swap);
5153 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5154 zend_sort((void *) lists[i], hash->nNumOfElements,
5155 sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5156 (swap_func_t)zend_hash_bucket_swap);
5157 }
5158 }
5159 }
5160
5161 /* copy the argument array if necessary */
5162 in_place = zend_may_modify_arg_in_place(&args[0]);
5163 if (in_place) {
5164 RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5165 } else {
5166 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5167 }
5168
5169 /* go through the lists and look for common values */
5170 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5171 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5172 && key_compare_type == INTERSECT_COMP_KEY_USER) {
5173 BG(user_compare_fci) = *fci_key;
5174 BG(user_compare_fci_cache) = *fci_key_cache;
5175 }
5176
5177 for (i = 1; i < arr_argc; i++) {
5178 if (behavior & INTERSECT_NORMAL) {
5179 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5180 ptrs[i]++;
5181 }
5182 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5183 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5184 ptrs[i]++;
5185 }
5186 if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5187 /* this means that ptrs[i] is not NULL so we can compare
5188 * and "c==0" is from last operation
5189 * in this branch of code we enter only when INTERSECT_ASSOC
5190 * since when we have INTERSECT_KEY compare of data is not wanted. */
5191 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5192 BG(user_compare_fci) = *fci_data;
5193 BG(user_compare_fci_cache) = *fci_data_cache;
5194 }
5195 if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5196 c = 1;
5197 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5198 BG(user_compare_fci) = *fci_key;
5199 BG(user_compare_fci_cache) = *fci_key_cache;
5200 /* When KEY_USER, the last parameter is always the callback */
5201 }
5202 /* we are going to the break */
5203 } else {
5204 /* continue looping */
5205 }
5206 }
5207 }
5208 if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5209 /* delete any values corresponding to remains of ptrs[0] */
5210 /* and exit because they do not present in at least one of */
5211 /* the other arguments */
5212 for (;;) {
5213 p = ptrs[0]++;
5214 if (Z_TYPE(p->val) == IS_UNDEF) {
5215 goto out;
5216 }
5217 if (p->key == NULL) {
5218 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5219 } else {
5220 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5221 }
5222 }
5223 }
5224 if (c) /* here we get if not all are equal */
5225 break;
5226 ptrs[i]++;
5227 }
5228 if (c) {
5229 /* Value of ptrs[0] not in all arguments, delete all entries */
5230 /* with value < value of ptrs[i] */
5231 for (;;) {
5232 p = ptrs[0];
5233 if (p->key == NULL) {
5234 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5235 } else {
5236 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5237 }
5238 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5239 goto out;
5240 }
5241 if (behavior == INTERSECT_NORMAL) {
5242 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5243 break;
5244 }
5245 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5246 /* no need of looping because indexes are unique */
5247 break;
5248 }
5249 }
5250 } else {
5251 /* ptrs[0] is present in all the arguments */
5252 /* Skip all entries with same value as ptrs[0] */
5253 for (;;) {
5254 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5255 goto out;
5256 }
5257 if (behavior == INTERSECT_NORMAL) {
5258 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5259 break;
5260 }
5261 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5262 /* no need of looping because indexes are unique */
5263 break;
5264 }
5265 }
5266 }
5267 }
5268 out:
5269 for (i = 0; i < arr_argc; i++) {
5270 hash = Z_ARRVAL(args[i]);
5271 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5272 }
5273
5274 PHP_ARRAY_CMP_FUNC_RESTORE();
5275
5276 efree(ptrs);
5277 efree(lists);
5278
5279 if (in_place) {
5280 Z_ADDREF_P(return_value);
5281 }
5282 }
5283 /* }}} */
5284
5285 /* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data. */
5286 PHP_FUNCTION(array_intersect_key)
5287 {
5288 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5289 }
5290 /* }}} */
5291
5292 /* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data. */
5293 PHP_FUNCTION(array_intersect_ukey)
5294 {
5295 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5296 }
5297 /* }}} */
5298
5299 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5300 PHP_FUNCTION(array_intersect)
5301 {
5302 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5303 }
5304 /* }}} */
5305
5306 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */
5307 PHP_FUNCTION(array_uintersect)
5308 {
5309 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5310 }
5311 /* }}} */
5312
5313 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5314 PHP_FUNCTION(array_intersect_assoc)
5315 {
5316 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5317 }
5318 /* }}} */
5319
5320 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using a user-supplied callback. */
5321 PHP_FUNCTION(array_intersect_uassoc)
5322 {
5323 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5324 }
5325 /* }}} */
5326
5327 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using a user-supplied callback. */
5328 PHP_FUNCTION(array_uintersect_assoc)
5329 {
5330 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5331 }
5332 /* }}} */
5333
5334 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks. */
5335 PHP_FUNCTION(array_uintersect_uassoc)
5336 {
5337 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5338 }
5339 /* }}} */
5340
5341 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5342 {
5343 uint32_t argc, i;
5344 zval *args;
5345 int (*diff_data_compare_func)(zval *, zval *) = NULL;
5346 bool ok;
5347 zval *val, *data;
5348 zend_string *key;
5349 zend_ulong h;
5350
5351 /* Get the argument count */
5352 argc = ZEND_NUM_ARGS();
5353 if (data_compare_type == DIFF_COMP_DATA_USER) {
5354 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5355 RETURN_THROWS();
5356 }
5357 diff_data_compare_func = zval_user_compare;
5358 } else {
5359 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5360 RETURN_THROWS();
5361 }
5362 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5363 diff_data_compare_func = zval_compare;
5364 }
5365 }
5366
5367 for (i = 0; i < argc; i++) {
5368 if (Z_TYPE(args[i]) != IS_ARRAY) {
5369 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5370 RETURN_THROWS();
5371 }
5372 }
5373
5374 array_init(return_value);
5375
5376 /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5377 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5378 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5379 val = Z_REFVAL_P(val);
5380 }
5381 if (key == NULL) {
5382 ok = 1;
5383 for (i = 1; i < argc; i++) {
5384 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5385 (!diff_data_compare_func ||
5386 diff_data_compare_func(val, data) == 0)
5387 ) {
5388 ok = 0;
5389 break;
5390 }
5391 }
5392 if (ok) {
5393 Z_TRY_ADDREF_P(val);
5394 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5395 }
5396 } else {
5397 ok = 1;
5398 for (i = 1; i < argc; i++) {
5399 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5400 (!diff_data_compare_func ||
5401 diff_data_compare_func(val, data) == 0)
5402 ) {
5403 ok = 0;
5404 break;
5405 }
5406 }
5407 if (ok) {
5408 Z_TRY_ADDREF_P(val);
5409 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5410 }
5411 }
5412 } ZEND_HASH_FOREACH_END();
5413 }
5414 /* }}} */
5415
5416 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5417 {
5418 zval *args = NULL;
5419 HashTable *hash;
5420 uint32_t arr_argc, i;
5421 int c;
5422 uint32_t idx;
5423 Bucket **lists, *list, **ptrs, *p;
5424 char *param_spec;
5425 zend_fcall_info fci1, fci2;
5426 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5427 zend_fcall_info *fci_key = NULL, *fci_data;
5428 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5429 PHP_ARRAY_CMP_FUNC_VARS;
5430
5431 bucket_compare_func_t diff_key_compare_func;
5432 bucket_compare_func_t diff_data_compare_func;
5433
5434 if (behavior == DIFF_NORMAL) {
5435 diff_key_compare_func = php_array_key_compare_string_unstable;
5436
5437 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5438 /* array_diff */
5439 param_spec = "+";
5440 diff_data_compare_func = php_array_data_compare_string_unstable;
5441 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5442 /* array_udiff */
5443 param_spec = "+f";
5444 diff_data_compare_func = php_array_user_compare_unstable;
5445 } else {
5446 ZEND_ASSERT(0 && "Invalid data_compare_type");
5447 return;
5448 }
5449
5450 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5451 RETURN_THROWS();
5452 }
5453 fci_data = &fci1;
5454 fci_data_cache = &fci1_cache;
5455
5456 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5457 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5458 * no comparison of the data is done (part of DIFF_ASSOC) */
5459
5460 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5461 /* array_diff_assoc() or array_diff_key() */
5462 param_spec = "+";
5463 diff_key_compare_func = php_array_key_compare_string_unstable;
5464 diff_data_compare_func = php_array_data_compare_string_unstable;
5465 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5466 /* array_udiff_assoc() */
5467 param_spec = "+f";
5468 diff_key_compare_func = php_array_key_compare_string_unstable;
5469 diff_data_compare_func = php_array_user_compare_unstable;
5470 fci_data = &fci1;
5471 fci_data_cache = &fci1_cache;
5472 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5473 /* array_diff_uassoc() or array_diff_ukey() */
5474 param_spec = "+f";
5475 diff_key_compare_func = php_array_user_key_compare_unstable;
5476 diff_data_compare_func = php_array_data_compare_string_unstable;
5477 fci_key = &fci1;
5478 fci_key_cache = &fci1_cache;
5479 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5480 /* array_udiff_uassoc() */
5481 param_spec = "+ff";
5482 diff_key_compare_func = php_array_user_key_compare_unstable;
5483 diff_data_compare_func = php_array_user_compare_unstable;
5484 fci_data = &fci1;
5485 fci_data_cache = &fci1_cache;
5486 fci_key = &fci2;
5487 fci_key_cache = &fci2_cache;
5488 } else {
5489 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5490 return;
5491 }
5492
5493 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5494 RETURN_THROWS();
5495 }
5496
5497 } else {
5498 ZEND_ASSERT(0 && "Invalid behavior");
5499 return;
5500 }
5501
5502 PHP_ARRAY_CMP_FUNC_BACKUP();
5503
5504 /* for each argument, create and sort list with pointers to the hash buckets */
5505 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5506 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5507
5508 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5509 BG(user_compare_fci) = *fci_data;
5510 BG(user_compare_fci_cache) = *fci_data_cache;
5511 } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5512 BG(user_compare_fci) = *fci_key;
5513 BG(user_compare_fci_cache) = *fci_key_cache;
5514 }
5515
5516 for (i = 0; i < arr_argc; i++) {
5517 if (Z_TYPE(args[i]) != IS_ARRAY) {
5518 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5519 arr_argc = i; /* only free up to i - 1 */
5520 goto out;
5521 }
5522 hash = Z_ARRVAL(args[i]);
5523 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5524 lists[i] = list;
5525 ptrs[i] = list;
5526 if (HT_IS_PACKED(hash)) {
5527 zval *zv = hash->arPacked;
5528 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5529 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5530 ZVAL_COPY_VALUE(&list->val, zv);
5531 list->h = idx;
5532 list->key = NULL;
5533 list++;
5534 }
5535 } else {
5536 p = hash->arData;
5537 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5538 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5539 *list++ = *p;
5540 }
5541 }
5542 ZVAL_UNDEF(&list->val);
5543 if (hash->nNumOfElements > 1) {
5544 if (behavior == DIFF_NORMAL) {
5545 zend_sort((void *) lists[i], hash->nNumOfElements,
5546 sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5547 (swap_func_t)zend_hash_bucket_swap);
5548 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5549 zend_sort((void *) lists[i], hash->nNumOfElements,
5550 sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5551 (swap_func_t)zend_hash_bucket_swap);
5552 }
5553 }
5554 }
5555
5556 /* copy the argument array */
5557 RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5558
5559 /* go through the lists and look for values of ptr[0] that are not in the others */
5560 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5561 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5562 &&
5563 key_compare_type == DIFF_COMP_KEY_USER
5564 ) {
5565 BG(user_compare_fci) = *fci_key;
5566 BG(user_compare_fci_cache) = *fci_key_cache;
5567 }
5568 c = 1;
5569 for (i = 1; i < arr_argc; i++) {
5570 Bucket *ptr = ptrs[i];
5571 if (behavior == DIFF_NORMAL) {
5572 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5573 ptrs[i]++;
5574 }
5575 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5576 while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5577 ptr++;
5578 }
5579 }
5580 if (!c) {
5581 if (behavior == DIFF_NORMAL) {
5582 if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5583 ptrs[i]++;
5584 }
5585 break;
5586 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
5587 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5588 * data comparison is not needed - skipped. */
5589 if (Z_TYPE(ptr->val) != IS_UNDEF) {
5590 if (data_compare_type == DIFF_COMP_DATA_USER) {
5591 BG(user_compare_fci) = *fci_data;
5592 BG(user_compare_fci_cache) = *fci_data_cache;
5593 }
5594 if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5595 /* the data is not the same */
5596 c = -1;
5597 if (key_compare_type == DIFF_COMP_KEY_USER) {
5598 BG(user_compare_fci) = *fci_key;
5599 BG(user_compare_fci_cache) = *fci_key_cache;
5600 }
5601 } else {
5602 break;
5603 /* we have found the element in other arrays thus we don't want it
5604 * in the return_value -> delete from there */
5605 }
5606 }
5607 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5608 /* the behavior here differs from INTERSECT_KEY in php_intersect
5609 * since in the "diff" case we have to remove the entry from
5610 * return_value while when doing intersection the entry must not
5611 * be deleted. */
5612 break; /* remove the key */
5613 }
5614 }
5615 }
5616 if (!c) {
5617 /* ptrs[0] in one of the other arguments */
5618 /* delete all entries with value as ptrs[0] */
5619 for (;;) {
5620 p = ptrs[0];
5621 if (p->key == NULL) {
5622 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5623 } else {
5624 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5625 }
5626 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5627 goto out;
5628 }
5629 if (behavior == DIFF_NORMAL) {
5630 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5631 break;
5632 }
5633 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5634 /* in this case no array_key_compare is needed */
5635 break;
5636 }
5637 }
5638 } else {
5639 /* ptrs[0] in none of the other arguments */
5640 /* skip all entries with value as ptrs[0] */
5641 for (;;) {
5642 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5643 goto out;
5644 }
5645 if (behavior == DIFF_NORMAL) {
5646 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5647 break;
5648 }
5649 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5650 /* in this case no array_key_compare is needed */
5651 break;
5652 }
5653 }
5654 }
5655 }
5656 out:
5657 for (i = 0; i < arr_argc; i++) {
5658 hash = Z_ARRVAL(args[i]);
5659 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5660 }
5661
5662 PHP_ARRAY_CMP_FUNC_RESTORE();
5663
5664 efree(ptrs);
5665 efree(lists);
5666 }
5667 /* }}} */
5668
5669 /* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved. */
5670 PHP_FUNCTION(array_diff_key)
5671 {
5672 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5673 }
5674 /* }}} */
5675
5676 /* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved. */
5677 PHP_FUNCTION(array_diff_ukey)
5678 {
5679 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5680 }
5681 /* }}} */
5682
5683 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5684 PHP_FUNCTION(array_diff)
5685 {
5686 zval *args;
5687 uint32_t argc, i;
5688 uint32_t num;
5689 HashTable exclude;
5690 zval *value;
5691 zend_string *str, *tmp_str, *key;
5692 zend_long idx;
5693 zval dummy;
5694
5695 ZEND_PARSE_PARAMETERS_START(1, -1)
5696 Z_PARAM_VARIADIC('+', args, argc)
5697 ZEND_PARSE_PARAMETERS_END();
5698
5699 if (Z_TYPE(args[0]) != IS_ARRAY) {
5700 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5701 RETURN_THROWS();
5702 }
5703
5704 num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5705 if (num == 0) {
5706 for (i = 1; i < argc; i++) {
5707 if (Z_TYPE(args[i]) != IS_ARRAY) {
5708 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5709 RETURN_THROWS();
5710 }
5711 }
5712 RETURN_EMPTY_ARRAY();
5713 } else if (num == 1) {
5714 int found = 0;
5715 zend_string *search_str, *tmp_search_str;
5716
5717 value = NULL;
5718 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5719 break;
5720 } ZEND_HASH_FOREACH_END();
5721
5722 if (!value) {
5723 for (i = 1; i < argc; i++) {
5724 if (Z_TYPE(args[i]) != IS_ARRAY) {
5725 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5726 RETURN_THROWS();
5727 }
5728 }
5729 RETURN_EMPTY_ARRAY();
5730 }
5731
5732 search_str = zval_get_tmp_string(value, &tmp_search_str);
5733
5734 for (i = 1; i < argc; i++) {
5735 if (Z_TYPE(args[i]) != IS_ARRAY) {
5736 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5737 RETURN_THROWS();
5738 }
5739 if (!found) {
5740 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5741 str = zval_get_tmp_string(value, &tmp_str);
5742 if (zend_string_equals(search_str, str)) {
5743 zend_tmp_string_release(tmp_str);
5744 found = 1;
5745 break;
5746 }
5747 zend_tmp_string_release(tmp_str);
5748 } ZEND_HASH_FOREACH_END();
5749 }
5750 }
5751
5752 zend_tmp_string_release(tmp_search_str);
5753
5754 if (found) {
5755 RETVAL_EMPTY_ARRAY();
5756 } else {
5757 ZVAL_COPY(return_value, &args[0]);
5758 }
5759 return;
5760 }
5761
5762 /* count number of elements */
5763 num = 0;
5764 for (i = 1; i < argc; i++) {
5765 if (Z_TYPE(args[i]) != IS_ARRAY) {
5766 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5767 RETURN_THROWS();
5768 }
5769 num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5770 }
5771
5772 if (num == 0) {
5773 ZVAL_COPY(return_value, &args[0]);
5774 return;
5775 }
5776
5777 ZVAL_NULL(&dummy);
5778 /* create exclude map */
5779 zend_hash_init(&exclude, num, NULL, NULL, 0);
5780 for (i = 1; i < argc; i++) {
5781 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5782 str = zval_get_tmp_string(value, &tmp_str);
5783 zend_hash_add(&exclude, str, &dummy);
5784 zend_tmp_string_release(tmp_str);
5785 } ZEND_HASH_FOREACH_END();
5786 }
5787
5788 /* copy all elements of first array that are not in exclude set */
5789 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5790 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5791 str = zval_get_tmp_string(value, &tmp_str);
5792 if (!zend_hash_exists(&exclude, str)) {
5793 if (key) {
5794 value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5795 } else {
5796 value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5797 }
5798 zval_add_ref(value);
5799 }
5800 zend_tmp_string_release(tmp_str);
5801 } ZEND_HASH_FOREACH_END();
5802
5803 zend_hash_destroy(&exclude);
5804 }
5805 /* }}} */
5806
5807 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function. */
5808 PHP_FUNCTION(array_udiff)
5809 {
5810 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5811 }
5812 /* }}} */
5813
5814 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal */
5815 PHP_FUNCTION(array_diff_assoc)
5816 {
5817 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5818 }
5819 /* }}} */
5820
5821 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function. */
5822 PHP_FUNCTION(array_diff_uassoc)
5823 {
5824 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5825 }
5826 /* }}} */
5827
5828 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function. */
5829 PHP_FUNCTION(array_udiff_assoc)
5830 {
5831 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5832 }
5833 /* }}} */
5834
5835 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions. */
5836 PHP_FUNCTION(array_udiff_uassoc)
5837 {
5838 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5839 }
5840 /* }}} */
5841
5842 #define MULTISORT_ORDER 0
5843 #define MULTISORT_TYPE 1
5844 #define MULTISORT_LAST 2
5845
5846 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5847 {
5848 Bucket *ab = *(Bucket **)a;
5849 Bucket *bb = *(Bucket **)b;
5850 int r;
5851 zend_long result;
5852
5853 r = 0;
5854 do {
5855 result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5856 if (result != 0) {
5857 return result > 0 ? 1 : -1;
5858 }
5859 r++;
5860 } while (Z_TYPE(ab[r].val) != IS_UNDEF);
5861
5862 return stable_sort_fallback(&ab[r], &bb[r]);
5863 }
5864 /* }}} */
5865
5866 #define MULTISORT_ABORT \
5867 efree(func); \
5868 efree(arrays); \
5869 return;
5870
5871 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5872 Bucket *t;
5873 Bucket **f = (Bucket **)p;
5874 Bucket **g = (Bucket **)q;
5875
5876 t = *f;
5877 *f = *g;
5878 *g = t;
5879 }
5880 /* }}} */
5881
5882 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5883 PHP_FUNCTION(array_multisort)
5884 {
5885 zval* args;
5886 zval** arrays;
5887 Bucket** indirect;
5888 uint32_t idx;
5889 HashTable* hash;
5890 uint32_t argc;
5891 uint32_t array_size;
5892 uint32_t num_arrays = 0;
5893 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
5894 int sort_order = PHP_SORT_ASC;
5895 int sort_type = PHP_SORT_REGULAR;
5896 uint32_t i, k, n;
5897 bucket_compare_func_t *func;
5898
5899 ZEND_PARSE_PARAMETERS_START(1, -1)
5900 Z_PARAM_VARIADIC('+', args, argc)
5901 ZEND_PARSE_PARAMETERS_END();
5902
5903 /* Allocate space for storing pointers to input arrays and sort flags. */
5904 arrays = (zval **)ecalloc(argc, sizeof(zval *));
5905 for (i = 0; i < MULTISORT_LAST; i++) {
5906 parse_state[i] = 0;
5907 }
5908 func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
5909
5910 /* Here we go through the input arguments and parse them. Each one can
5911 * be either an array or a sort flag which follows an array. If not
5912 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5913 * accordingly. There can't be two sort flags of the same type after an
5914 * array, and the very first argument has to be an array. */
5915 for (i = 0; i < argc; i++) {
5916 zval *arg = &args[i];
5917
5918 ZVAL_DEREF(arg);
5919 if (Z_TYPE_P(arg) == IS_ARRAY) {
5920 SEPARATE_ARRAY(arg);
5921 /* We see the next array, so we update the sort flags of
5922 * the previous array and reset the sort flags. */
5923 if (i > 0) {
5924 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5925 sort_order = PHP_SORT_ASC;
5926 sort_type = PHP_SORT_REGULAR;
5927 }
5928 arrays[num_arrays++] = arg;
5929
5930 /* Next one may be an array or a list of sort flags. */
5931 for (k = 0; k < MULTISORT_LAST; k++) {
5932 parse_state[k] = 1;
5933 }
5934 } else if (Z_TYPE_P(arg) == IS_LONG) {
5935 switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5936 case PHP_SORT_ASC:
5937 case PHP_SORT_DESC:
5938 /* flag allowed here */
5939 if (parse_state[MULTISORT_ORDER] == 1) {
5940 /* Save the flag and make sure then next arg is not the current flag. */
5941 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5942 parse_state[MULTISORT_ORDER] = 0;
5943 } else {
5944 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5945 MULTISORT_ABORT;
5946 }
5947 break;
5948
5949 case PHP_SORT_REGULAR:
5950 case PHP_SORT_NUMERIC:
5951 case PHP_SORT_STRING:
5952 case PHP_SORT_NATURAL:
5953 case PHP_SORT_LOCALE_STRING:
5954 /* flag allowed here */
5955 if (parse_state[MULTISORT_TYPE] == 1) {
5956 /* Save the flag and make sure then next arg is not the current flag. */
5957 sort_type = (int)Z_LVAL_P(arg);
5958 parse_state[MULTISORT_TYPE] = 0;
5959 } else {
5960 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5961 MULTISORT_ABORT;
5962 }
5963 break;
5964
5965 default:
5966 zend_argument_value_error(i + 1, "must be a valid sort flag");
5967 MULTISORT_ABORT;
5968 break;
5969
5970 }
5971 } else {
5972 zend_argument_type_error(i + 1, "must be an array or a sort flag");
5973 MULTISORT_ABORT;
5974 }
5975 }
5976 /* Take care of the last array sort flags. */
5977 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5978
5979 /* Make sure the arrays are of the same size. */
5980 array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
5981 for (i = 1; i < num_arrays; i++) {
5982 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
5983 zend_value_error("Array sizes are inconsistent");
5984 MULTISORT_ABORT;
5985 }
5986 }
5987
5988 /* If all arrays are empty we don't need to do anything. */
5989 if (array_size < 1) {
5990 efree(func);
5991 efree(arrays);
5992 RETURN_TRUE;
5993 }
5994
5995 /* Create the indirection array. This array is of size MxN, where
5996 * M is the number of entries in each input array and N is the number
5997 * of the input arrays + 1. The last column is UNDEF to indicate the end
5998 * of the row. It also stores the original position for stable sorting. */
5999 indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6000 /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6001 Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6002 for (i = 0; i < array_size; i++) {
6003 indirect[i] = indirects + (i * (num_arrays + 1));
6004 }
6005 for (i = 0; i < num_arrays; i++) {
6006 k = 0;
6007 if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6008 zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6009 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6010 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6011 ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6012 indirect[k][i].h = idx;
6013 indirect[k][i].key = NULL;
6014 k++;
6015 }
6016 } else {
6017 Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6018 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6019 if (Z_TYPE(p->val) == IS_UNDEF) continue;
6020 indirect[k][i] = *p;
6021 k++;
6022 }
6023 }
6024 }
6025 for (k = 0; k < array_size; k++) {
6026 ZVAL_UNDEF(&indirect[k][num_arrays].val);
6027 Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6028 }
6029
6030 /* Do the actual sort magic - bada-bim, bada-boom. */
6031 zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6032 if (EG(exception)) {
6033 goto clean_up;
6034 }
6035
6036 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6037 for (i = 0; i < num_arrays; i++) {
6038 hash = Z_ARRVAL_P(arrays[i]);
6039 hash->nNumUsed = array_size;
6040 hash->nNextFreeElement = array_size;
6041 hash->nInternalPointer = 0;
6042 if (HT_IS_PACKED(hash)) {
6043 for (k = 0; k < array_size; k++) {
6044 ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6045 }
6046 } else {
6047 bool repack = true;
6048
6049 for (n = 0, k = 0; k < array_size; k++) {
6050 hash->arData[k] = indirect[k][i];
6051 if (hash->arData[k].key == NULL) {
6052 hash->arData[k].h = n++;
6053 } else {
6054 repack = false;
6055 }
6056 }
6057 if (repack) {
6058 zend_hash_to_packed(hash);
6059 } else {
6060 zend_hash_rehash(hash);
6061 }
6062 }
6063 }
6064 RETVAL_TRUE;
6065
6066 clean_up:
6067 efree(indirects);
6068 efree(indirect);
6069 efree(func);
6070 efree(arrays);
6071 }
6072 /* }}} */
6073
6074 /* {{{ php_array_pick_keys */
6075 PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent)
6076 {
6077 HashTable *ht = Z_ARRVAL_P(input);
6078 uint32_t num_avail = zend_hash_num_elements(ht);
6079 zend_long i, randval;
6080 zend_string *string_key;
6081 zend_ulong num_key;
6082 zval *zv;
6083 Bucket *b;
6084 zend_bitset bitset;
6085 int negative_bitset = 0;
6086 uint32_t bitset_len;
6087 ALLOCA_FLAG(use_heap);
6088
6089 if (num_avail == 0) {
6090 if (!silent) {
6091 zend_argument_value_error(1, "cannot be empty");
6092 }
6093 return false;
6094 }
6095
6096 if (num_req == 1) {
6097 if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6098 /* If less than 1/2 of elements are used, don't sample. Instead search for a
6099 * specific offset using linear scan. */
6100 i = 0;
6101 randval = algo->range(status, 0, num_avail - 1);
6102 if (EG(exception)) {
6103 return false;
6104 }
6105 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6106 if (i == randval) {
6107 if (string_key) {
6108 ZVAL_STR_COPY(retval, string_key);
6109 } else {
6110 ZVAL_LONG(retval, num_key);
6111 }
6112 return true;
6113 }
6114 i++;
6115 } ZEND_HASH_FOREACH_END();
6116 }
6117
6118 /* Sample random buckets until we hit one that is not empty.
6119 * The worst case probability of hitting an empty element is 1-1/2. The worst case
6120 * probability of hitting N empty elements in a row is (1-1/2)**N.
6121 * For N=10 this becomes smaller than 0.1%. */
6122 if (HT_IS_PACKED(ht)) {
6123 do {
6124 randval = algo->range(status, 0, ht->nNumUsed - 1);
6125 if (EG(exception)) {
6126 return false;
6127 }
6128 zv = &ht->arPacked[randval];
6129 if (!Z_ISUNDEF_P(zv)) {
6130 ZVAL_LONG(retval, randval);
6131 return true;
6132 }
6133 } while (true);
6134 } else {
6135 do {
6136 randval = algo->range(status, 0, ht->nNumUsed - 1);
6137 if (EG(exception)) {
6138 return false;
6139 }
6140 b = &ht->arData[randval];
6141 if (!Z_ISUNDEF(b->val)) {
6142 if (b->key) {
6143 ZVAL_STR_COPY(retval, b->key);
6144 } else {
6145 ZVAL_LONG(retval, b->h);
6146 }
6147 return true;
6148 }
6149 } while (true);
6150 }
6151 }
6152
6153 if (num_req <= 0 || num_req > num_avail) {
6154 if (!silent) {
6155 zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6156 }
6157 return false;
6158 }
6159
6160 /* Make the return value an array only if we need to pass back more than one result. */
6161 array_init_size(retval, (uint32_t) num_req);
6162 if (num_req > (num_avail >> 1)) {
6163 negative_bitset = 1;
6164 num_req = num_avail - num_req;
6165 }
6166
6167 bitset_len = zend_bitset_len(num_avail);
6168 bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6169 zend_bitset_clear(bitset, bitset_len);
6170
6171 i = num_req;
6172 int failures = 0;
6173 while (i) {
6174 randval = algo->range(status, 0, num_avail - 1);
6175 if (EG(exception)) {
6176 goto fail;
6177 }
6178 if (zend_bitset_in(bitset, randval)) {
6179 if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6180 if (!silent) {
6181 zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6182 }
6183
6184 goto fail;
6185 }
6186 } else {
6187 zend_bitset_incl(bitset, randval);
6188 i--;
6189 failures = 0;
6190 }
6191 }
6192
6193 zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6194 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6195 /* We can't use zend_hash_index_find()
6196 * because the array may have string keys or gaps. */
6197 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6198 if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6199 if (string_key) {
6200 ZEND_HASH_FILL_SET_STR_COPY(string_key);
6201 } else {
6202 ZEND_HASH_FILL_SET_LONG(num_key);
6203 }
6204 ZEND_HASH_FILL_NEXT();
6205 }
6206 i++;
6207 } ZEND_HASH_FOREACH_END();
6208 } ZEND_HASH_FILL_END();
6209
6210 free_alloca(bitset, use_heap);
6211
6212 return true;
6213
6214 fail:
6215 free_alloca(bitset, use_heap);
6216
6217 return false;
6218 }
6219 /* }}} */
6220
6221 /* {{{ Return key/keys for random entry/entries in the array */
6222 PHP_FUNCTION(array_rand)
6223 {
6224 zval *input;
6225 zend_long num_req = 1;
6226
6227 ZEND_PARSE_PARAMETERS_START(1, 2)
6228 Z_PARAM_ARRAY(input)
6229 Z_PARAM_OPTIONAL
6230 Z_PARAM_LONG(num_req)
6231 ZEND_PARSE_PARAMETERS_END();
6232
6233 if (!php_array_pick_keys(
6234 php_random_default_algo(),
6235 php_random_default_status(),
6236 input,
6237 num_req,
6238 return_value,
6239 false)
6240 ) {
6241 RETURN_THROWS();
6242 }
6243 }
6244 /* }}} */
6245
6246 /* Wrapper for array_sum and array_product */
6247 static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6248 {
6249 HashTable *input;
6250 zval *entry;
6251
6252 ZEND_PARSE_PARAMETERS_START(1, 1)
6253 Z_PARAM_ARRAY_HT(input)
6254 ZEND_PARSE_PARAMETERS_END();
6255
6256 if (zend_hash_num_elements(input) == 0) {
6257 RETURN_LONG(initial);
6258 }
6259
6260 ZVAL_LONG(return_value, initial);
6261 ZEND_HASH_FOREACH_VAL(input, entry) {
6262 /* For objects we try to cast them to a numeric type */
6263 if (Z_TYPE_P(entry) == IS_OBJECT) {
6264 zval dst;
6265 zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6266
6267 /* Do not type error for BC */
6268 if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6269 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6270 op_name, zend_zval_type_name(entry));
6271 continue;
6272 }
6273 op(return_value, return_value, &dst);
6274 continue;
6275 }
6276
6277 zend_result status = op(return_value, return_value, entry);
6278 if (status == FAILURE) {
6279 ZEND_ASSERT(EG(exception));
6280 zend_clear_exception();
6281 /* BC resources: previously resources were cast to int */
6282 if (Z_TYPE_P(entry) == IS_RESOURCE) {
6283 zval tmp;
6284 ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6285 op(return_value, return_value, &tmp);
6286 }
6287 /* BC non numeric strings: previously were cast to 0 */
6288 else if (Z_TYPE_P(entry) == IS_STRING) {
6289 zval tmp;
6290 ZVAL_LONG(&tmp, 0);
6291 op(return_value, return_value, &tmp);
6292 }
6293 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6294 op_name, zend_zval_type_name(entry));
6295 }
6296 } ZEND_HASH_FOREACH_END();
6297 }
6298
6299 /* {{{ Returns the sum of the array entries */
6300 PHP_FUNCTION(array_sum)
6301 {
6302 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6303 }
6304 /* }}} */
6305
6306 /* {{{ Returns the product of the array entries */
6307 PHP_FUNCTION(array_product)
6308 {
6309 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6310 }
6311 /* }}} */
6312
6313 /* {{{ Iteratively reduce the array to a single value via the callback. */
6314 PHP_FUNCTION(array_reduce)
6315 {
6316 zval *input;
6317 zval args[2];
6318 zval *operand;
6319 zval retval;
6320 zend_fcall_info fci;
6321 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6322 zval *initial = NULL;
6323 HashTable *htbl;
6324
6325 ZEND_PARSE_PARAMETERS_START(2, 3)
6326 Z_PARAM_ARRAY(input)
6327 Z_PARAM_FUNC(fci, fci_cache)
6328 Z_PARAM_OPTIONAL
6329 Z_PARAM_ZVAL(initial)
6330 ZEND_PARSE_PARAMETERS_END();
6331
6332
6333 if (ZEND_NUM_ARGS() > 2) {
6334 ZVAL_COPY(return_value, initial);
6335 } else {
6336 ZVAL_NULL(return_value);
6337 }
6338
6339 /* (zval **)input points to an element of argument stack
6340 * the base pointer of which is subject to change.
6341 * thus we need to keep the pointer to the hashtable for safety */
6342 htbl = Z_ARRVAL_P(input);
6343
6344 if (zend_hash_num_elements(htbl) == 0) {
6345 return;
6346 }
6347
6348 fci.retval = &retval;
6349 fci.param_count = 2;
6350
6351 ZEND_HASH_FOREACH_VAL(htbl, operand) {
6352 ZVAL_COPY_VALUE(&args[0], return_value);
6353 ZVAL_COPY(&args[1], operand);
6354 fci.params = args;
6355
6356 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6357 zval_ptr_dtor(&args[1]);
6358 zval_ptr_dtor(&args[0]);
6359 ZVAL_COPY_VALUE(return_value, &retval);
6360 if (UNEXPECTED(Z_ISREF_P(return_value))) {
6361 zend_unwrap_reference(return_value);
6362 }
6363 } else {
6364 zval_ptr_dtor(&args[1]);
6365 zval_ptr_dtor(&args[0]);
6366 RETURN_NULL();
6367 }
6368 } ZEND_HASH_FOREACH_END();
6369 }
6370 /* }}} */
6371
6372 /* {{{ Filters elements from the array via the callback. */
6373 PHP_FUNCTION(array_filter)
6374 {
6375 zval *array;
6376 zval *operand;
6377 zval *key;
6378 zval args[2];
6379 zval retval;
6380 bool have_callback = 0;
6381 zend_long use_type = 0;
6382 zend_string *string_key;
6383 zend_fcall_info fci = empty_fcall_info;
6384 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6385 zend_ulong num_key;
6386
6387 ZEND_PARSE_PARAMETERS_START(1, 3)
6388 Z_PARAM_ARRAY(array)
6389 Z_PARAM_OPTIONAL
6390 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6391 Z_PARAM_LONG(use_type)
6392 ZEND_PARSE_PARAMETERS_END();
6393
6394 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6395 RETVAL_EMPTY_ARRAY();
6396 return;
6397 }
6398 array_init(return_value);
6399
6400 if (ZEND_FCI_INITIALIZED(fci)) {
6401 have_callback = 1;
6402 fci.retval = &retval;
6403 if (use_type == ARRAY_FILTER_USE_BOTH) {
6404 fci.param_count = 2;
6405 key = &args[1];
6406 } else {
6407 fci.param_count = 1;
6408 key = &args[0];
6409 }
6410 }
6411
6412 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6413 if (have_callback) {
6414 if (use_type) {
6415 /* Set up the key */
6416 if (!string_key) {
6417 ZVAL_LONG(key, num_key);
6418 } else {
6419 ZVAL_STR_COPY(key, string_key);
6420 }
6421 }
6422 if (use_type != ARRAY_FILTER_USE_KEY) {
6423 ZVAL_COPY(&args[0], operand);
6424 }
6425 fci.params = args;
6426
6427 if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6428 int retval_true;
6429
6430 zval_ptr_dtor(&args[0]);
6431 if (use_type == ARRAY_FILTER_USE_BOTH) {
6432 zval_ptr_dtor(&args[1]);
6433 }
6434 retval_true = zend_is_true(&retval);
6435 zval_ptr_dtor(&retval);
6436 if (!retval_true) {
6437 continue;
6438 }
6439 } else {
6440 zval_ptr_dtor(&args[0]);
6441 if (use_type == ARRAY_FILTER_USE_BOTH) {
6442 zval_ptr_dtor(&args[1]);
6443 }
6444 return;
6445 }
6446 } else if (!zend_is_true(operand)) {
6447 continue;
6448 }
6449
6450 if (string_key) {
6451 operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6452 } else {
6453 operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6454 }
6455 zval_add_ref(operand);
6456 } ZEND_HASH_FOREACH_END();
6457 }
6458 /* }}} */
6459
6460 /* {{{ Applies the callback to the elements in given arrays. */
6461 PHP_FUNCTION(array_map)
6462 {
6463 zval *arrays = NULL;
6464 int n_arrays = 0;
6465 zval result;
6466 zend_fcall_info fci = empty_fcall_info;
6467 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6468 int i;
6469 uint32_t k, maxlen = 0;
6470
6471 ZEND_PARSE_PARAMETERS_START(2, -1)
6472 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6473 Z_PARAM_VARIADIC('+', arrays, n_arrays)
6474 ZEND_PARSE_PARAMETERS_END();
6475
6476 if (n_arrays == 1) {
6477 zend_ulong num_key;
6478 zend_string *str_key;
6479 zval *zv, arg;
6480 int ret;
6481
6482 if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6483 zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6484 RETURN_THROWS();
6485 }
6486 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6487
6488 /* Short-circuit: if no callback and only one array, just return it. */
6489 if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6490 ZVAL_COPY(return_value, &arrays[0]);
6491 return;
6492 }
6493
6494 array_init_size(return_value, maxlen);
6495 zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6496
6497 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6498 fci.retval = &result;
6499 fci.param_count = 1;
6500 fci.params = &arg;
6501
6502 ZVAL_COPY(&arg, zv);
6503 ret = zend_call_function(&fci, &fci_cache);
6504 i_zval_ptr_dtor(&arg);
6505 if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6506 zend_array_destroy(Z_ARR_P(return_value));
6507 RETURN_NULL();
6508 }
6509 if (str_key) {
6510 _zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6511 } else {
6512 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6513 }
6514 } ZEND_HASH_FOREACH_END();
6515 } else {
6516 uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6517
6518 for (i = 0; i < n_arrays; i++) {
6519 if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6520 zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6521 efree(array_pos);
6522 RETURN_THROWS();
6523 }
6524 if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6525 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6526 }
6527 }
6528
6529 array_init_size(return_value, maxlen);
6530
6531 if (!ZEND_FCI_INITIALIZED(fci)) {
6532 zval zv;
6533
6534 /* We iterate through all the arrays at once. */
6535 for (k = 0; k < maxlen; k++) {
6536
6537 /* If no callback, the result will be an array, consisting of current
6538 * entries from all arrays. */
6539 array_init_size(&result, n_arrays);
6540
6541 for (i = 0; i < n_arrays; i++) {
6542 /* If this array still has elements, add the current one to the
6543 * parameter list, otherwise use null value. */
6544 uint32_t pos = array_pos[i];
6545 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6546 while (1) {
6547 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6548 ZVAL_NULL(&zv);
6549 break;
6550 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6551 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6552 array_pos[i] = pos + 1;
6553 break;
6554 }
6555 pos++;
6556 }
6557 } else {
6558 while (1) {
6559 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6560 ZVAL_NULL(&zv);
6561 break;
6562 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6563 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6564 array_pos[i] = pos + 1;
6565 break;
6566 }
6567 pos++;
6568 }
6569 }
6570 zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6571 }
6572
6573 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6574 }
6575 } else {
6576 zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6577
6578 /* We iterate through all the arrays at once. */
6579 for (k = 0; k < maxlen; k++) {
6580 for (i = 0; i < n_arrays; i++) {
6581 /* If this array still has elements, add the current one to the
6582 * parameter list, otherwise use null value. */
6583 uint32_t pos = array_pos[i];
6584 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6585 while (1) {
6586 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6587 ZVAL_NULL(¶ms[i]);
6588 break;
6589 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6590 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6591 array_pos[i] = pos + 1;
6592 break;
6593 }
6594 pos++;
6595 }
6596 } else {
6597 while (1) {
6598 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6599 ZVAL_NULL(¶ms[i]);
6600 break;
6601 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6602 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6603 array_pos[i] = pos + 1;
6604 break;
6605 }
6606 pos++;
6607 }
6608 }
6609 }
6610
6611 fci.retval = &result;
6612 fci.param_count = n_arrays;
6613 fci.params = params;
6614
6615 if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6616 efree(array_pos);
6617 zend_array_destroy(Z_ARR_P(return_value));
6618 for (i = 0; i < n_arrays; i++) {
6619 zval_ptr_dtor(¶ms[i]);
6620 }
6621 efree(params);
6622 RETURN_NULL();
6623 } else {
6624 for (i = 0; i < n_arrays; i++) {
6625 zval_ptr_dtor(¶ms[i]);
6626 }
6627 }
6628
6629 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6630 }
6631
6632 efree(params);
6633 }
6634 efree(array_pos);
6635 }
6636 }
6637 /* }}} */
6638
6639 /* {{{ Checks if the given key or index exists in the array */
6640 PHP_FUNCTION(array_key_exists)
6641 {
6642 zval *key;
6643 HashTable *ht;
6644
6645 ZEND_PARSE_PARAMETERS_START(2, 2)
6646 Z_PARAM_ZVAL(key)
6647 Z_PARAM_ARRAY_HT(ht)
6648 ZEND_PARSE_PARAMETERS_END();
6649
6650 switch (Z_TYPE_P(key)) {
6651 case IS_STRING:
6652 RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6653 break;
6654 case IS_LONG:
6655 RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6656 break;
6657 case IS_NULL:
6658 RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6659 break;
6660 case IS_DOUBLE:
6661 RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6662 break;
6663 case IS_FALSE:
6664 RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6665 break;
6666 case IS_TRUE:
6667 RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6668 break;
6669 case IS_RESOURCE:
6670 zend_use_resource_as_offset(key);
6671 RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6672 break;
6673 default:
6674 zend_argument_type_error(1, "must be a valid array offset type");
6675 break;
6676 }
6677 }
6678 /* }}} */
6679
6680 /* {{{ Split array into chunks */
6681 PHP_FUNCTION(array_chunk)
6682 {
6683 int num_in;
6684 zend_long size, current = 0;
6685 zend_string *str_key;
6686 zend_ulong num_key;
6687 bool preserve_keys = 0;
6688 zval *input = NULL;
6689 zval chunk;
6690 zval *entry;
6691
6692 ZEND_PARSE_PARAMETERS_START(2, 3)
6693 Z_PARAM_ARRAY(input)
6694 Z_PARAM_LONG(size)
6695 Z_PARAM_OPTIONAL
6696 Z_PARAM_BOOL(preserve_keys)
6697 ZEND_PARSE_PARAMETERS_END();
6698
6699 /* Do bounds checking for size parameter. */
6700 if (size < 1) {
6701 zend_argument_value_error(2, "must be greater than 0");
6702 RETURN_THROWS();
6703 }
6704
6705 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6706
6707 if (size > num_in) {
6708 if (num_in == 0) {
6709 RETVAL_EMPTY_ARRAY();
6710 return;
6711 }
6712 size = num_in;
6713 }
6714
6715 array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6716
6717 ZVAL_UNDEF(&chunk);
6718
6719 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6720 /* If new chunk, create and initialize it. */
6721 if (Z_TYPE(chunk) == IS_UNDEF) {
6722 array_init_size(&chunk, (uint32_t)size);
6723 }
6724
6725 /* Add entry to the chunk, preserving keys if necessary. */
6726 if (preserve_keys) {
6727 if (str_key) {
6728 entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6729 } else {
6730 entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6731 }
6732 } else {
6733 entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6734 }
6735 zval_add_ref(entry);
6736
6737 /* If reached the chunk size, add it to the result array, and reset the
6738 * pointer. */
6739 if (!(++current % size)) {
6740 add_next_index_zval(return_value, &chunk);
6741 ZVAL_UNDEF(&chunk);
6742 }
6743 } ZEND_HASH_FOREACH_END();
6744
6745 /* Add the final chunk if there is one. */
6746 if (Z_TYPE(chunk) != IS_UNDEF) {
6747 add_next_index_zval(return_value, &chunk);
6748 }
6749 }
6750 /* }}} */
6751
6752 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6753 PHP_FUNCTION(array_combine)
6754 {
6755 HashTable *values, *keys;
6756 uint32_t pos_values = 0;
6757 zval *entry_keys, *entry_values;
6758 int num_keys, num_values;
6759
6760 ZEND_PARSE_PARAMETERS_START(2, 2)
6761 Z_PARAM_ARRAY_HT(keys)
6762 Z_PARAM_ARRAY_HT(values)
6763 ZEND_PARSE_PARAMETERS_END();
6764
6765 num_keys = zend_hash_num_elements(keys);
6766 num_values = zend_hash_num_elements(values);
6767
6768 if (num_keys != num_values) {
6769 zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6770 RETURN_THROWS();
6771 }
6772
6773 if (!num_keys) {
6774 RETURN_EMPTY_ARRAY();
6775 }
6776
6777 array_init_size(return_value, num_keys);
6778 ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6779 while (1) {
6780 if (pos_values >= values->nNumUsed) {
6781 break;
6782 }
6783 entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6784 if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6785 if (Z_TYPE_P(entry_keys) == IS_LONG) {
6786 entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6787 Z_LVAL_P(entry_keys), entry_values);
6788 } else {
6789 zend_string *tmp_key;
6790 zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6791 entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6792 key, entry_values);
6793 zend_tmp_string_release(tmp_key);
6794 }
6795 zval_add_ref(entry_values);
6796 pos_values++;
6797 break;
6798 }
6799 pos_values++;
6800 }
6801 } ZEND_HASH_FOREACH_END();
6802 }
6803 /* }}} */
6804