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_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
709 } else {
710 zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
711 }
712
713 RETURN_TRUE;
714 }
715 /* }}} */
716
717 /* {{{ Sort an array using natural sort */
PHP_FUNCTION(natsort)718 PHP_FUNCTION(natsort)
719 {
720 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
721 }
722 /* }}} */
723
724 /* {{{ Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)725 PHP_FUNCTION(natcasesort)
726 {
727 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
728 }
729 /* }}} */
730
731 /* {{{ Sort an array and maintain index association */
PHP_FUNCTION(asort)732 PHP_FUNCTION(asort)
733 {
734 zval *array;
735 zend_long sort_type = PHP_SORT_REGULAR;
736 bucket_compare_func_t cmp;
737
738 ZEND_PARSE_PARAMETERS_START(1, 2)
739 Z_PARAM_ARRAY_EX(array, 0, 1)
740 Z_PARAM_OPTIONAL
741 Z_PARAM_LONG(sort_type)
742 ZEND_PARSE_PARAMETERS_END();
743
744 cmp = php_get_data_compare_func(sort_type, 0);
745
746 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
747
748 RETURN_TRUE;
749 }
750 /* }}} */
751
752 /* {{{ Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)753 PHP_FUNCTION(arsort)
754 {
755 zval *array;
756 zend_long sort_type = PHP_SORT_REGULAR;
757 bucket_compare_func_t cmp;
758
759 ZEND_PARSE_PARAMETERS_START(1, 2)
760 Z_PARAM_ARRAY_EX(array, 0, 1)
761 Z_PARAM_OPTIONAL
762 Z_PARAM_LONG(sort_type)
763 ZEND_PARSE_PARAMETERS_END();
764
765 cmp = php_get_data_compare_func(sort_type, 1);
766
767 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
768
769 RETURN_TRUE;
770 }
771 /* }}} */
772
773 /* {{{ Sort an array */
PHP_FUNCTION(sort)774 PHP_FUNCTION(sort)
775 {
776 zval *array;
777 zend_long sort_type = PHP_SORT_REGULAR;
778 bucket_compare_func_t cmp;
779
780 ZEND_PARSE_PARAMETERS_START(1, 2)
781 Z_PARAM_ARRAY_EX(array, 0, 1)
782 Z_PARAM_OPTIONAL
783 Z_PARAM_LONG(sort_type)
784 ZEND_PARSE_PARAMETERS_END();
785
786 cmp = php_get_data_compare_func(sort_type, 0);
787
788 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
789
790 RETURN_TRUE;
791 }
792 /* }}} */
793
794 /* {{{ Sort an array in reverse order */
PHP_FUNCTION(rsort)795 PHP_FUNCTION(rsort)
796 {
797 zval *array;
798 zend_long sort_type = PHP_SORT_REGULAR;
799 bucket_compare_func_t cmp;
800
801 ZEND_PARSE_PARAMETERS_START(1, 2)
802 Z_PARAM_ARRAY_EX(array, 0, 1)
803 Z_PARAM_OPTIONAL
804 Z_PARAM_LONG(sort_type)
805 ZEND_PARSE_PARAMETERS_END();
806
807 cmp = php_get_data_compare_func(sort_type, 1);
808
809 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
810
811 RETURN_TRUE;
812 }
813 /* }}} */
814
php_array_user_compare_unstable(Bucket * f,Bucket * s)815 static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
816 {
817 zval args[2];
818 zval retval;
819 bool call_failed;
820
821 ZVAL_COPY(&args[0], &f->val);
822 ZVAL_COPY(&args[1], &s->val);
823
824 BG(user_compare_fci).param_count = 2;
825 BG(user_compare_fci).params = args;
826 BG(user_compare_fci).retval = &retval;
827 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
828 zval_ptr_dtor(&args[1]);
829 zval_ptr_dtor(&args[0]);
830 if (UNEXPECTED(call_failed)) {
831 return 0;
832 }
833
834 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
835 if (!ARRAYG(compare_deprecation_thrown)) {
836 php_error_docref(NULL, E_DEPRECATED,
837 "Returning bool from comparison function is deprecated, "
838 "return an integer less than, equal to, or greater than zero");
839 ARRAYG(compare_deprecation_thrown) = 1;
840 }
841
842 if (Z_TYPE(retval) == IS_FALSE) {
843 /* Retry with swapped operands. */
844 ZVAL_COPY(&args[0], &s->val);
845 ZVAL_COPY(&args[1], &f->val);
846 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
847 zval_ptr_dtor(&args[1]);
848 zval_ptr_dtor(&args[0]);
849 if (call_failed) {
850 return 0;
851 }
852
853 zend_long ret = zval_get_long(&retval);
854 zval_ptr_dtor(&retval);
855 return -ZEND_NORMALIZE_BOOL(ret);
856 }
857 }
858
859 zend_long ret = zval_get_long(&retval);
860 zval_ptr_dtor(&retval);
861 return ZEND_NORMALIZE_BOOL(ret);
862 }
863 /* }}} */
864
php_array_user_compare(Bucket * a,Bucket * b)865 static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
866 {
867 RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
868 }
869 /* }}} */
870
871 #define PHP_ARRAY_CMP_FUNC_VARS \
872 zend_fcall_info old_user_compare_fci; \
873 zend_fcall_info_cache old_user_compare_fci_cache \
874
875 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
876 old_user_compare_fci = BG(user_compare_fci); \
877 old_user_compare_fci_cache = BG(user_compare_fci_cache); \
878 ARRAYG(compare_deprecation_thrown) = 0; \
879 BG(user_compare_fci_cache) = empty_fcall_info_cache; \
880
881 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
882 BG(user_compare_fci) = old_user_compare_fci; \
883 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
884
php_usort(INTERNAL_FUNCTION_PARAMETERS,bucket_compare_func_t compare_func,bool renumber)885 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, bool renumber) /* {{{ */
886 {
887 zval *array;
888 zend_array *arr;
889 PHP_ARRAY_CMP_FUNC_VARS;
890
891 PHP_ARRAY_CMP_FUNC_BACKUP();
892
893 ZEND_PARSE_PARAMETERS_START(2, 2)
894 Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
895 Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
896 ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
897
898 arr = Z_ARR_P(array);
899 if (zend_hash_num_elements(arr) == 0) {
900 PHP_ARRAY_CMP_FUNC_RESTORE();
901 RETURN_TRUE;
902 }
903
904 /* Copy array, so the in-place modifications will not be visible to the callback function */
905 arr = zend_array_dup(arr);
906
907 zend_hash_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 SEPARATE_ZVAL(dest_entry);
3920 dest_zval = dest_entry;
3921
3922 if (Z_TYPE_P(dest_zval) == IS_NULL) {
3923 convert_to_array(dest_zval);
3924 add_next_index_null(dest_zval);
3925 } else {
3926 convert_to_array(dest_zval);
3927 }
3928 ZVAL_UNDEF(&tmp);
3929 if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3930 ZVAL_COPY(&tmp, src_zval);
3931 convert_to_array(&tmp);
3932 src_zval = &tmp;
3933 }
3934 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3935 if (thash) {
3936 GC_TRY_PROTECT_RECURSION(thash);
3937 }
3938 ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3939 if (thash) {
3940 GC_TRY_UNPROTECT_RECURSION(thash);
3941 }
3942 if (!ret) {
3943 return 0;
3944 }
3945 } else {
3946 Z_TRY_ADDREF_P(src_zval);
3947 zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3948 if (EXPECTED(!zv)) {
3949 Z_TRY_DELREF_P(src_zval);
3950 zend_cannot_add_element();
3951 return 0;
3952 }
3953 }
3954 zval_ptr_dtor(&tmp);
3955 } else {
3956 zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3957 zval_add_ref(zv);
3958 }
3959 } else {
3960 zval *zv = zend_hash_next_index_insert(dest, src_entry);
3961 if (UNEXPECTED(!zv)) {
3962 zend_cannot_add_element();
3963 return 0;
3964 }
3965 zval_add_ref(zv);
3966 }
3967 } ZEND_HASH_FOREACH_END();
3968 return 1;
3969 }
3970 /* }}} */
3971
3972 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3973 {
3974 zval *src_entry;
3975 zend_string *string_key;
3976
3977 if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
3978 zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
3979 ZEND_HASH_FILL_PACKED(dest) {
3980 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3981 if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
3982 UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
3983 src_entry = Z_REFVAL_P(src_entry);
3984 }
3985 Z_TRY_ADDREF_P(src_entry);
3986 ZEND_HASH_FILL_ADD(src_entry);
3987 } ZEND_HASH_FOREACH_END();
3988 } ZEND_HASH_FILL_END();
3989 } else {
3990 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3991 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3992 Z_REFCOUNT_P(src_entry) == 1)) {
3993 src_entry = Z_REFVAL_P(src_entry);
3994 }
3995 Z_TRY_ADDREF_P(src_entry);
3996 if (string_key) {
3997 zend_hash_update(dest, string_key, src_entry);
3998 } else {
3999 zend_hash_next_index_insert_new(dest, src_entry);
4000 }
4001 } ZEND_HASH_FOREACH_END();
4002 }
4003 return 1;
4004 }
4005 /* }}} */
4006
4007 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4008 {
4009 zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4010 zend_string *string_key;
4011 zend_ulong num_key;
4012 int ret;
4013
4014 ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4015 src_zval = src_entry;
4016 ZVAL_DEREF(src_zval);
4017 if (string_key) {
4018 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4019 (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4020 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4021 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4022
4023 zval *zv = zend_hash_update(dest, string_key, src_entry);
4024 zval_add_ref(zv);
4025 continue;
4026 }
4027 } else {
4028 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4029 (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4030 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4031 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4032
4033 zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4034 zval_add_ref(zv);
4035 continue;
4036 }
4037 }
4038
4039 dest_zval = dest_entry;
4040 ZVAL_DEREF(dest_zval);
4041 if (Z_IS_RECURSIVE_P(dest_zval) ||
4042 Z_IS_RECURSIVE_P(src_zval) ||
4043 (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))) {
4044 zend_throw_error(NULL, "Recursion detected");
4045 return 0;
4046 }
4047
4048 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4049 SEPARATE_ZVAL(dest_entry);
4050 dest_zval = dest_entry;
4051
4052 if (Z_REFCOUNTED_P(dest_zval)) {
4053 Z_PROTECT_RECURSION_P(dest_zval);
4054 }
4055 if (Z_REFCOUNTED_P(src_zval)) {
4056 Z_PROTECT_RECURSION_P(src_zval);
4057 }
4058
4059 ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4060
4061 if (Z_REFCOUNTED_P(dest_zval)) {
4062 Z_UNPROTECT_RECURSION_P(dest_zval);
4063 }
4064 if (Z_REFCOUNTED_P(src_zval)) {
4065 Z_UNPROTECT_RECURSION_P(src_zval);
4066 }
4067
4068 if (!ret) {
4069 return 0;
4070 }
4071 } ZEND_HASH_FOREACH_END();
4072
4073 return 1;
4074 }
4075 /* }}} */
4076
4077 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4078 {
4079 zval *args = NULL;
4080 zval *arg;
4081 uint32_t argc, i;
4082 HashTable *dest;
4083
4084 ZEND_PARSE_PARAMETERS_START(1, -1)
4085 Z_PARAM_VARIADIC('+', args, argc)
4086 ZEND_PARSE_PARAMETERS_END();
4087
4088
4089 for (i = 0; i < argc; i++) {
4090 zval *arg = args + i;
4091
4092 if (Z_TYPE_P(arg) != IS_ARRAY) {
4093 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4094 RETURN_THROWS();
4095 }
4096 }
4097
4098 /* copy first array if necessary */
4099 arg = args;
4100 bool in_place = zend_may_modify_arg_in_place(arg);
4101 if (in_place) {
4102 dest = Z_ARRVAL_P(arg);
4103 } else {
4104 dest = zend_array_dup(Z_ARRVAL_P(arg));
4105 }
4106
4107 ZVAL_ARR(return_value, dest);
4108
4109 if (recursive) {
4110 for (i = 1; i < argc; i++) {
4111 arg = args + i;
4112 php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4113 }
4114 } else {
4115 for (i = 1; i < argc; i++) {
4116 arg = args + i;
4117 zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4118 }
4119 }
4120
4121 if (in_place) {
4122 GC_ADDREF(dest);
4123 }
4124 }
4125 /* }}} */
4126
4127 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4128 {
4129 zval *args = NULL;
4130 zval *arg;
4131 uint32_t argc, i;
4132 zval *src_entry;
4133 HashTable *src, *dest;
4134 uint32_t count = 0;
4135
4136 ZEND_PARSE_PARAMETERS_START(0, -1)
4137 Z_PARAM_VARIADIC('+', args, argc)
4138 ZEND_PARSE_PARAMETERS_END();
4139
4140 if (argc == 0) {
4141 RETURN_EMPTY_ARRAY();
4142 }
4143
4144 for (i = 0; i < argc; i++) {
4145 zval *arg = args + i;
4146
4147 if (Z_TYPE_P(arg) != IS_ARRAY) {
4148 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4149 RETURN_THROWS();
4150 }
4151 count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4152 }
4153
4154 if (argc == 2) {
4155 zval *ret = NULL;
4156
4157 if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4158 ret = &args[1];
4159 } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4160 ret = &args[0];
4161 }
4162 if (ret) {
4163 if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4164 if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4165 ZVAL_COPY(return_value, ret);
4166 return;
4167 }
4168 } else {
4169 bool copy = 1;
4170 zend_string *string_key;
4171
4172 ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4173 if (!string_key) {
4174 copy = 0;
4175 break;
4176 }
4177 } ZEND_HASH_FOREACH_END();
4178 if (copy) {
4179 ZVAL_COPY(return_value, ret);
4180 return;
4181 }
4182 }
4183 }
4184 }
4185
4186 arg = args;
4187 src = Z_ARRVAL_P(arg);
4188 /* copy first array if necessary */
4189 bool in_place = false;
4190 if (HT_IS_PACKED(src)) {
4191 /* Note: If it has holes, it might get sequentialized */
4192 if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4193 dest = src;
4194 in_place = true;
4195 ZVAL_ARR(return_value, dest);
4196 } else {
4197 array_init_size(return_value, count);
4198 dest = Z_ARRVAL_P(return_value);
4199
4200 zend_hash_real_init_packed(dest);
4201 ZEND_HASH_FILL_PACKED(dest) {
4202 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4203 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4204 Z_REFCOUNT_P(src_entry) == 1)) {
4205 src_entry = Z_REFVAL_P(src_entry);
4206 }
4207 Z_TRY_ADDREF_P(src_entry);
4208 ZEND_HASH_FILL_ADD(src_entry);
4209 } ZEND_HASH_FOREACH_END();
4210 } ZEND_HASH_FILL_END();
4211 }
4212 } else {
4213 array_init_size(return_value, count);
4214 dest = Z_ARRVAL_P(return_value);
4215
4216 zend_string *string_key;
4217 zend_hash_real_init_mixed(dest);
4218 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4219 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4220 Z_REFCOUNT_P(src_entry) == 1)) {
4221 src_entry = Z_REFVAL_P(src_entry);
4222 }
4223 Z_TRY_ADDREF_P(src_entry);
4224 if (EXPECTED(string_key)) {
4225 _zend_hash_append(dest, string_key, src_entry);
4226 } else {
4227 zend_hash_next_index_insert_new(dest, src_entry);
4228 }
4229 } ZEND_HASH_FOREACH_END();
4230 }
4231 if (recursive) {
4232 for (i = 1; i < argc; i++) {
4233 arg = args + i;
4234 php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4235 }
4236 } else {
4237 for (i = 1; i < argc; i++) {
4238 arg = args + i;
4239 php_array_merge(dest, Z_ARRVAL_P(arg));
4240 }
4241 }
4242
4243 if (in_place) {
4244 GC_ADDREF(dest);
4245 }
4246 }
4247 /* }}} */
4248
4249 /* {{{ Merges elements from passed arrays into one array */
4250 PHP_FUNCTION(array_merge)
4251 {
4252 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4253 }
4254 /* }}} */
4255
4256 /* {{{ Recursively merges elements from passed arrays into one array */
4257 PHP_FUNCTION(array_merge_recursive)
4258 {
4259 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4260 }
4261 /* }}} */
4262
4263 /* {{{ Replaces elements from passed arrays into one array */
4264 PHP_FUNCTION(array_replace)
4265 {
4266 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4267 }
4268 /* }}} */
4269
4270 /* {{{ Recursively replaces elements from passed arrays into one array */
4271 PHP_FUNCTION(array_replace_recursive)
4272 {
4273 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4274 }
4275 /* }}} */
4276
4277 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4278 PHP_FUNCTION(array_keys)
4279 {
4280 zval *input, /* Input array */
4281 *search_value = NULL, /* Value to search for */
4282 *entry, /* An entry in the input array */
4283 new_val; /* New value */
4284 bool strict = 0; /* do strict comparison */
4285 zend_ulong num_idx;
4286 zend_string *str_idx;
4287 zend_array *arrval;
4288 zend_ulong elem_count;
4289
4290 ZEND_PARSE_PARAMETERS_START(1, 3)
4291 Z_PARAM_ARRAY(input)
4292 Z_PARAM_OPTIONAL
4293 Z_PARAM_ZVAL(search_value)
4294 Z_PARAM_BOOL(strict)
4295 ZEND_PARSE_PARAMETERS_END();
4296 arrval = Z_ARRVAL_P(input);
4297 elem_count = zend_hash_num_elements(arrval);
4298
4299 /* Base case: empty input */
4300 if (!elem_count) {
4301 RETURN_COPY(input);
4302 }
4303
4304 /* Initialize return array */
4305 if (search_value != NULL) {
4306 array_init(return_value);
4307
4308 if (strict) {
4309 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4310 ZVAL_DEREF(entry);
4311 if (fast_is_identical_function(search_value, entry)) {
4312 if (str_idx) {
4313 ZVAL_STR_COPY(&new_val, str_idx);
4314 } else {
4315 ZVAL_LONG(&new_val, num_idx);
4316 }
4317 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4318 }
4319 } ZEND_HASH_FOREACH_END();
4320 } else {
4321 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4322 if (fast_equal_check_function(search_value, entry)) {
4323 if (str_idx) {
4324 ZVAL_STR_COPY(&new_val, str_idx);
4325 } else {
4326 ZVAL_LONG(&new_val, num_idx);
4327 }
4328 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4329 }
4330 } ZEND_HASH_FOREACH_END();
4331 }
4332 } else {
4333 array_init_size(return_value, elem_count);
4334 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4335 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4336 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4337 /* Optimistic case: range(0..n-1) for vector-like packed array */
4338 zend_ulong lval = 0;
4339
4340 for (; lval < elem_count; ++lval) {
4341 ZEND_HASH_FILL_SET_LONG(lval);
4342 ZEND_HASH_FILL_NEXT();
4343 }
4344 } else {
4345 /* Go through input array and add keys to the return array */
4346 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4347 if (str_idx) {
4348 ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4349 } else {
4350 ZEND_HASH_FILL_SET_LONG(num_idx);
4351 }
4352 ZEND_HASH_FILL_NEXT();
4353 } ZEND_HASH_FOREACH_END();
4354 }
4355 } ZEND_HASH_FILL_END();
4356 }
4357 }
4358 /* }}} */
4359
4360 /* {{{ Get the key of the first element of the array */
4361 PHP_FUNCTION(array_key_first)
4362 {
4363 zval *stack; /* Input stack */
4364
4365 ZEND_PARSE_PARAMETERS_START(1, 1)
4366 Z_PARAM_ARRAY(stack)
4367 ZEND_PARSE_PARAMETERS_END();
4368
4369 HashTable *target_hash = Z_ARRVAL_P (stack);
4370 HashPosition pos = 0;
4371 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4372 }
4373 /* }}} */
4374
4375 /* {{{ Get the key of the last element of the array */
4376 PHP_FUNCTION(array_key_last)
4377 {
4378 zval *stack; /* Input stack */
4379 HashPosition pos;
4380
4381 ZEND_PARSE_PARAMETERS_START(1, 1)
4382 Z_PARAM_ARRAY(stack)
4383 ZEND_PARSE_PARAMETERS_END();
4384
4385 HashTable *target_hash = Z_ARRVAL_P (stack);
4386 zend_hash_internal_pointer_end_ex(target_hash, &pos);
4387 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4388 }
4389 /* }}} */
4390
4391 /* {{{ Return just the values from the input array */
4392 PHP_FUNCTION(array_values)
4393 {
4394 zval *input; /* Input array */
4395 zend_array *arrval;
4396 zend_long arrlen;
4397
4398 ZEND_PARSE_PARAMETERS_START(1, 1)
4399 Z_PARAM_ARRAY(input)
4400 ZEND_PARSE_PARAMETERS_END();
4401
4402 arrval = Z_ARRVAL_P(input);
4403
4404 /* Return empty input as is */
4405 arrlen = zend_hash_num_elements(arrval);
4406 if (!arrlen) {
4407 RETURN_EMPTY_ARRAY();
4408 }
4409
4410 /* Return vector-like packed arrays as-is */
4411 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4412 arrval->nNextFreeElement == arrlen) {
4413 RETURN_COPY(input);
4414 }
4415
4416 RETURN_ARR(zend_array_to_list(arrval));
4417 }
4418 /* }}} */
4419
4420 /* {{{ Return the value as key and the frequency of that value in input as value */
4421 PHP_FUNCTION(array_count_values)
4422 {
4423 zval *input, /* Input array */
4424 *entry, /* An entry in the input array */
4425 *tmp;
4426 HashTable *myht;
4427
4428 ZEND_PARSE_PARAMETERS_START(1, 1)
4429 Z_PARAM_ARRAY(input)
4430 ZEND_PARSE_PARAMETERS_END();
4431
4432 /* Initialize return array */
4433 array_init(return_value);
4434
4435 /* Go through input array and add values to the return array */
4436 myht = Z_ARRVAL_P(input);
4437 ZEND_HASH_FOREACH_VAL(myht, entry) {
4438 ZVAL_DEREF(entry);
4439 if (Z_TYPE_P(entry) == IS_LONG) {
4440 if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4441 zval data;
4442 ZVAL_LONG(&data, 1);
4443 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4444 } else {
4445 Z_LVAL_P(tmp)++;
4446 }
4447 } else if (Z_TYPE_P(entry) == IS_STRING) {
4448 if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4449 zval data;
4450 ZVAL_LONG(&data, 1);
4451 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4452 } else {
4453 Z_LVAL_P(tmp)++;
4454 }
4455 } else {
4456 php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4457 }
4458 } ZEND_HASH_FOREACH_END();
4459 }
4460 /* }}} */
4461
4462 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4463 {
4464 zval *prop = NULL;
4465
4466 if (Z_TYPE_P(data) == IS_OBJECT) {
4467 zend_string *tmp_str;
4468 /* If name is an integer convert integer to string */
4469 if (name_str == NULL) {
4470 tmp_str = zend_long_to_str(name_long);
4471 } else {
4472 tmp_str = zend_string_copy(name_str);
4473 }
4474 /* The has_property check is first performed in "exists" mode (which returns true for
4475 * properties that are null but exist) and then in "has" mode to handle objects that
4476 * implement __isset (which is not called in "exists" mode). */
4477 if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4478 || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4479 prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4480 if (prop) {
4481 ZVAL_DEREF(prop);
4482 if (prop != rv) {
4483 Z_TRY_ADDREF_P(prop);
4484 }
4485 }
4486 }
4487 zend_string_release(tmp_str);
4488 } else if (Z_TYPE_P(data) == IS_ARRAY) {
4489 /* Name is a string */
4490 if (name_str != NULL) {
4491 prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4492 } else {
4493 prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4494 }
4495 if (prop) {
4496 ZVAL_DEREF(prop);
4497 Z_TRY_ADDREF_P(prop);
4498 }
4499 }
4500
4501 return prop;
4502 }
4503 /* }}} */
4504
4505 /* {{{ Return the values from a single column in the input array, identified by the
4506 value_key and optionally indexed by the index_key */
4507 PHP_FUNCTION(array_column)
4508 {
4509 HashTable *input;
4510 zval *colval, *data, rv;
4511 zend_string *column_str = NULL;
4512 zend_long column_long = 0;
4513 bool column_is_null = 0;
4514 zend_string *index_str = NULL;
4515 zend_long index_long = 0;
4516 bool index_is_null = 1;
4517
4518 ZEND_PARSE_PARAMETERS_START(2, 3)
4519 Z_PARAM_ARRAY_HT(input)
4520 Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4521 Z_PARAM_OPTIONAL
4522 Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4523 ZEND_PARSE_PARAMETERS_END();
4524
4525 void* cache_slot_column[3] = { NULL, NULL, NULL };
4526 void* cache_slot_index[3] = { NULL, NULL, NULL };
4527
4528 array_init_size(return_value, zend_hash_num_elements(input));
4529 /* Index param is not passed */
4530 if (index_is_null) {
4531 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4532 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4533 ZEND_HASH_FOREACH_VAL(input, data) {
4534 ZVAL_DEREF(data);
4535 if (column_is_null) {
4536 Z_TRY_ADDREF_P(data);
4537 colval = data;
4538 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4539 continue;
4540 }
4541 ZEND_HASH_FILL_ADD(colval);
4542 } ZEND_HASH_FOREACH_END();
4543 } ZEND_HASH_FILL_END();
4544 } else {
4545 ZEND_HASH_FOREACH_VAL(input, data) {
4546 ZVAL_DEREF(data);
4547
4548 if (column_is_null) {
4549 Z_TRY_ADDREF_P(data);
4550 colval = data;
4551 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4552 continue;
4553 }
4554
4555 zval rv;
4556 zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4557 if (keyval) {
4558 array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4559 zval_ptr_dtor(colval);
4560 zval_ptr_dtor(keyval);
4561 } else {
4562 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4563 }
4564 } ZEND_HASH_FOREACH_END();
4565 }
4566 }
4567 /* }}} */
4568
4569 /* {{{ Return input as a new array with the order of the entries reversed */
4570 PHP_FUNCTION(array_reverse)
4571 {
4572 zval *input, /* Input array */
4573 *entry; /* An entry in the input array */
4574 zend_string *string_key;
4575 zend_ulong num_key;
4576 bool preserve_keys = 0; /* whether to preserve keys */
4577
4578 ZEND_PARSE_PARAMETERS_START(1, 2)
4579 Z_PARAM_ARRAY(input)
4580 Z_PARAM_OPTIONAL
4581 Z_PARAM_BOOL(preserve_keys)
4582 ZEND_PARSE_PARAMETERS_END();
4583
4584 /* Initialize return array */
4585 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4586 if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4587 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4588 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4589 ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4590 if (UNEXPECTED(Z_ISREF_P(entry) &&
4591 Z_REFCOUNT_P(entry) == 1)) {
4592 entry = Z_REFVAL_P(entry);
4593 }
4594 Z_TRY_ADDREF_P(entry);
4595 ZEND_HASH_FILL_ADD(entry);
4596 } ZEND_HASH_FOREACH_END();
4597 } ZEND_HASH_FILL_END();
4598 } else {
4599 ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4600 if (string_key) {
4601 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4602 } else {
4603 if (preserve_keys) {
4604 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4605 } else {
4606 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4607 }
4608 }
4609 zval_add_ref(entry);
4610 } ZEND_HASH_FOREACH_END();
4611 }
4612 }
4613 /* }}} */
4614
4615 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4616 PHP_FUNCTION(array_pad)
4617 {
4618 zval *input; /* Input array */
4619 zval *pad_value; /* Padding value obviously */
4620 zend_long pad_size; /* Size to pad to */
4621 zend_long pad_size_abs; /* Absolute value of pad_size */
4622 zend_long input_size; /* Size of the input array */
4623 zend_long num_pads; /* How many pads do we need */
4624 zend_long i;
4625 zend_string *key;
4626 zval *value;
4627
4628 ZEND_PARSE_PARAMETERS_START(3, 3)
4629 Z_PARAM_ARRAY(input)
4630 Z_PARAM_LONG(pad_size)
4631 Z_PARAM_ZVAL(pad_value)
4632 ZEND_PARSE_PARAMETERS_END();
4633
4634 if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4635 zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4636 RETURN_THROWS();
4637 }
4638
4639 /* Do some initial calculations */
4640 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4641 pad_size_abs = ZEND_ABS(pad_size);
4642
4643 if (input_size >= pad_size_abs) {
4644 /* Copy the original array */
4645 ZVAL_COPY(return_value, input);
4646 return;
4647 }
4648
4649 num_pads = pad_size_abs - input_size;
4650 if (Z_REFCOUNTED_P(pad_value)) {
4651 GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4652 }
4653
4654 array_init_size(return_value, pad_size_abs);
4655 if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4656 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4657
4658 if (pad_size < 0) {
4659 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4660 for (i = 0; i < num_pads; i++) {
4661 ZEND_HASH_FILL_ADD(pad_value);
4662 }
4663 } ZEND_HASH_FILL_END();
4664 }
4665
4666 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4667 ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4668 Z_TRY_ADDREF_P(value);
4669 ZEND_HASH_FILL_ADD(value);
4670 } ZEND_HASH_FOREACH_END();
4671 } ZEND_HASH_FILL_END();
4672
4673 if (pad_size > 0) {
4674 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4675 for (i = 0; i < num_pads; i++) {
4676 ZEND_HASH_FILL_ADD(pad_value);
4677 }
4678 } ZEND_HASH_FILL_END();
4679 }
4680 } else {
4681 if (pad_size < 0) {
4682 for (i = 0; i < num_pads; i++) {
4683 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4684 }
4685 }
4686
4687 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4688 Z_TRY_ADDREF_P(value);
4689 if (key) {
4690 zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4691 } else {
4692 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4693 }
4694 } ZEND_HASH_FOREACH_END();
4695
4696 if (pad_size > 0) {
4697 for (i = 0; i < num_pads; i++) {
4698 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4699 }
4700 }
4701 }
4702 }
4703 /* }}} */
4704
4705 /* {{{ Return array with key <-> value flipped */
4706 PHP_FUNCTION(array_flip)
4707 {
4708 zval *array, *entry, data;
4709 zend_ulong num_idx;
4710 zend_string *str_idx;
4711
4712 ZEND_PARSE_PARAMETERS_START(1, 1)
4713 Z_PARAM_ARRAY(array)
4714 ZEND_PARSE_PARAMETERS_END();
4715
4716 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4717
4718 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4719 ZVAL_DEREF(entry);
4720 if (Z_TYPE_P(entry) == IS_LONG) {
4721 if (str_idx) {
4722 ZVAL_STR_COPY(&data, str_idx);
4723 } else {
4724 ZVAL_LONG(&data, num_idx);
4725 }
4726 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4727 } else if (Z_TYPE_P(entry) == IS_STRING) {
4728 if (str_idx) {
4729 ZVAL_STR_COPY(&data, str_idx);
4730 } else {
4731 ZVAL_LONG(&data, num_idx);
4732 }
4733 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4734 } else {
4735 php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4736 }
4737 } ZEND_HASH_FOREACH_END();
4738 }
4739 /* }}} */
4740
4741 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4742 PHP_FUNCTION(array_change_key_case)
4743 {
4744 zval *array, *entry;
4745 zend_string *string_key;
4746 zend_string *new_key;
4747 zend_ulong num_key;
4748 zend_long change_to_upper=0;
4749
4750 ZEND_PARSE_PARAMETERS_START(1, 2)
4751 Z_PARAM_ARRAY(array)
4752 Z_PARAM_OPTIONAL
4753 Z_PARAM_LONG(change_to_upper)
4754 ZEND_PARSE_PARAMETERS_END();
4755
4756 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4757
4758 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4759 if (!string_key) {
4760 entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4761 } else {
4762 if (change_to_upper) {
4763 new_key = zend_string_toupper(string_key);
4764 } else {
4765 new_key = zend_string_tolower(string_key);
4766 }
4767 entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4768 zend_string_release_ex(new_key, 0);
4769 }
4770
4771 zval_add_ref(entry);
4772 } ZEND_HASH_FOREACH_END();
4773 }
4774 /* }}} */
4775
4776 struct bucketindex {
4777 Bucket b;
4778 unsigned int i;
4779 };
4780
4781 static void array_bucketindex_swap(void *p, void *q)
4782 {
4783 struct bucketindex *f = (struct bucketindex *)p;
4784 struct bucketindex *g = (struct bucketindex *)q;
4785 struct bucketindex t;
4786 t = *f;
4787 *f = *g;
4788 *g = t;
4789 }
4790
4791 /* {{{ Removes duplicate values from array */
4792 PHP_FUNCTION(array_unique)
4793 {
4794 zval *array;
4795 Bucket *p;
4796 zend_long sort_type = PHP_SORT_STRING;
4797 bucket_compare_func_t cmp;
4798 struct bucketindex *arTmp, *cmpdata, *lastkept;
4799 uint32_t i, idx;
4800
4801 ZEND_PARSE_PARAMETERS_START(1, 2)
4802 Z_PARAM_ARRAY(array)
4803 Z_PARAM_OPTIONAL
4804 Z_PARAM_LONG(sort_type)
4805 ZEND_PARSE_PARAMETERS_END();
4806
4807 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4808 ZVAL_COPY(return_value, array);
4809 return;
4810 }
4811
4812 if (sort_type == PHP_SORT_STRING) {
4813 HashTable seen;
4814 zend_long num_key;
4815 zend_string *str_key;
4816 zval *val;
4817
4818 zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4819 array_init(return_value);
4820
4821 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4822 zval *retval;
4823 if (Z_TYPE_P(val) == IS_STRING) {
4824 retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4825 } else {
4826 zend_string *tmp_str_val;
4827 zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4828 retval = zend_hash_add_empty_element(&seen, str_val);
4829 zend_tmp_string_release(tmp_str_val);
4830 }
4831
4832 if (retval) {
4833 /* First occurrence of the value */
4834 if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4835 ZVAL_DEREF(val);
4836 }
4837 Z_TRY_ADDREF_P(val);
4838
4839 if (str_key) {
4840 zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4841 } else {
4842 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4843 }
4844 }
4845 } ZEND_HASH_FOREACH_END();
4846
4847 zend_hash_destroy(&seen);
4848 return;
4849 }
4850
4851 cmp = php_get_data_compare_func_unstable(sort_type, 0);
4852
4853 bool in_place = zend_may_modify_arg_in_place(array);
4854 if (in_place) {
4855 RETVAL_ARR(Z_ARRVAL_P(array));
4856 } else {
4857 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4858 }
4859
4860 /* create and sort array with pointers to the target_hash buckets */
4861 arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4862 if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4863 zval *zv = Z_ARRVAL_P(array)->arPacked;
4864 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4865 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4866 ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4867 arTmp[i].b.h = idx;
4868 arTmp[i].b.key = NULL;
4869 arTmp[i].i = i;
4870 i++;
4871 }
4872 } else {
4873 p = Z_ARRVAL_P(array)->arData;
4874 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4875 if (Z_TYPE(p->val) == IS_UNDEF) continue;
4876 arTmp[i].b = *p;
4877 arTmp[i].i = i;
4878 i++;
4879 }
4880 }
4881 ZVAL_UNDEF(&arTmp[i].b.val);
4882 zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4883 (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4884 /* go through the sorted array and delete duplicates from the copy */
4885 lastkept = arTmp;
4886 for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4887 if (cmp(&lastkept->b, &cmpdata->b)) {
4888 lastkept = cmpdata;
4889 } else {
4890 if (lastkept->i > cmpdata->i) {
4891 p = &lastkept->b;
4892 lastkept = cmpdata;
4893 } else {
4894 p = &cmpdata->b;
4895 }
4896 if (p->key == NULL) {
4897 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4898 } else {
4899 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4900 }
4901 }
4902 }
4903 pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4904
4905 if (in_place) {
4906 Z_ADDREF_P(return_value);
4907 }
4908 }
4909 /* }}} */
4910
4911 static int zval_compare(zval *first, zval *second) /* {{{ */
4912 {
4913 return string_compare_function(first, second);
4914 }
4915 /* }}} */
4916
4917 static int zval_user_compare(zval *a, zval *b) /* {{{ */
4918 {
4919 zval args[2];
4920 zval retval;
4921
4922 ZVAL_COPY_VALUE(&args[0], a);
4923 ZVAL_COPY_VALUE(&args[1], b);
4924
4925 BG(user_compare_fci).param_count = 2;
4926 BG(user_compare_fci).params = args;
4927 BG(user_compare_fci).retval = &retval;
4928
4929 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4930 zend_long ret = zval_get_long(&retval);
4931 zval_ptr_dtor(&retval);
4932 return ZEND_NORMALIZE_BOOL(ret);
4933 } else {
4934 return 0;
4935 }
4936 }
4937 /* }}} */
4938
4939 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4940 {
4941 uint32_t argc, i;
4942 zval *args;
4943 int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4944 bool ok;
4945 zval *val, *data;
4946 char *param_spec;
4947 zend_string *key;
4948 zend_ulong h;
4949
4950 /* Get the argument count */
4951 argc = ZEND_NUM_ARGS();
4952 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4953 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
4954 param_spec = "+f";
4955 intersect_data_compare_func = zval_user_compare;
4956 } else {
4957 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
4958 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
4959 param_spec = "+";
4960
4961 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4962 intersect_data_compare_func = zval_compare;
4963 }
4964 }
4965
4966 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4967 RETURN_THROWS();
4968 }
4969
4970 for (i = 0; i < argc; i++) {
4971 if (Z_TYPE(args[i]) != IS_ARRAY) {
4972 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
4973 RETURN_THROWS();
4974 }
4975 }
4976
4977 array_init(return_value);
4978
4979 /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
4980 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4981 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4982 val = Z_REFVAL_P(val);
4983 }
4984 if (key == NULL) {
4985 ok = 1;
4986 for (i = 1; i < argc; i++) {
4987 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
4988 (intersect_data_compare_func &&
4989 intersect_data_compare_func(val, data) != 0)
4990 ) {
4991 ok = 0;
4992 break;
4993 }
4994 }
4995 if (ok) {
4996 Z_TRY_ADDREF_P(val);
4997 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4998 }
4999 } else {
5000 ok = 1;
5001 for (i = 1; i < argc; i++) {
5002 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5003 (intersect_data_compare_func &&
5004 intersect_data_compare_func(val, data) != 0)
5005 ) {
5006 ok = 0;
5007 break;
5008 }
5009 }
5010 if (ok) {
5011 Z_TRY_ADDREF_P(val);
5012 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5013 }
5014 }
5015 } ZEND_HASH_FOREACH_END();
5016 }
5017 /* }}} */
5018
5019 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5020 {
5021 zval *args = NULL;
5022 HashTable *hash;
5023 uint32_t arr_argc, i;
5024 int c = 0;
5025 uint32_t idx;
5026 Bucket **lists, *list, **ptrs, *p;
5027 char *param_spec;
5028 zend_fcall_info fci1, fci2;
5029 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5030 zend_fcall_info *fci_key = NULL, *fci_data;
5031 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5032 PHP_ARRAY_CMP_FUNC_VARS;
5033 bool in_place = false;
5034
5035 bucket_compare_func_t intersect_key_compare_func;
5036 bucket_compare_func_t intersect_data_compare_func;
5037
5038 if (behavior == INTERSECT_NORMAL) {
5039 intersect_key_compare_func = php_array_key_compare_string;
5040
5041 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5042 /* array_intersect() */
5043 param_spec = "+";
5044 intersect_data_compare_func = php_array_data_compare_string_unstable;
5045 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5046 /* array_uintersect() */
5047 param_spec = "+f";
5048 intersect_data_compare_func = php_array_user_compare_unstable;
5049 } else {
5050 ZEND_ASSERT(0 && "Invalid data_compare_type");
5051 return;
5052 }
5053
5054 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5055 RETURN_THROWS();
5056 }
5057 fci_data = &fci1;
5058 fci_data_cache = &fci1_cache;
5059
5060 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5061 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5062 * no comparison of the data is done (part of INTERSECT_ASSOC) */
5063
5064 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5065 /* array_intersect_assoc() or array_intersect_key() */
5066 param_spec = "+";
5067 intersect_key_compare_func = php_array_key_compare_string_unstable;
5068 intersect_data_compare_func = php_array_data_compare_string_unstable;
5069 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5070 /* array_uintersect_assoc() */
5071 param_spec = "+f";
5072 intersect_key_compare_func = php_array_key_compare_string_unstable;
5073 intersect_data_compare_func = php_array_user_compare_unstable;
5074 fci_data = &fci1;
5075 fci_data_cache = &fci1_cache;
5076 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5077 /* array_intersect_uassoc() or array_intersect_ukey() */
5078 param_spec = "+f";
5079 intersect_key_compare_func = php_array_user_key_compare_unstable;
5080 intersect_data_compare_func = php_array_data_compare_string_unstable;
5081 fci_key = &fci1;
5082 fci_key_cache = &fci1_cache;
5083 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5084 /* array_uintersect_uassoc() */
5085 param_spec = "+ff";
5086 intersect_key_compare_func = php_array_user_key_compare_unstable;
5087 intersect_data_compare_func = php_array_user_compare_unstable;
5088 fci_data = &fci1;
5089 fci_data_cache = &fci1_cache;
5090 fci_key = &fci2;
5091 fci_key_cache = &fci2_cache;
5092 } else {
5093 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5094 return;
5095 }
5096
5097 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5098 RETURN_THROWS();
5099 }
5100
5101 } else {
5102 ZEND_ASSERT(0 && "Invalid behavior");
5103 return;
5104 }
5105
5106 PHP_ARRAY_CMP_FUNC_BACKUP();
5107
5108 /* for each argument, create and sort list with pointers to the hash buckets */
5109 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5110 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5111
5112 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5113 BG(user_compare_fci) = *fci_data;
5114 BG(user_compare_fci_cache) = *fci_data_cache;
5115 } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5116 BG(user_compare_fci) = *fci_key;
5117 BG(user_compare_fci_cache) = *fci_key_cache;
5118 }
5119
5120 for (i = 0; i < arr_argc; i++) {
5121 if (Z_TYPE(args[i]) != IS_ARRAY) {
5122 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5123 arr_argc = i; /* only free up to i - 1 */
5124 goto out;
5125 }
5126 hash = Z_ARRVAL(args[i]);
5127 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5128 lists[i] = list;
5129 ptrs[i] = list;
5130 if (HT_IS_PACKED(hash)) {
5131 zval *zv = hash->arPacked;
5132 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5133 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5134 ZVAL_COPY_VALUE(&list->val, zv);
5135 list->h = idx;
5136 list->key = NULL;
5137 list++;
5138 }
5139 } else {
5140 p = hash->arData;
5141 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5142 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5143 *list++ = *p;
5144 }
5145 }
5146 ZVAL_UNDEF(&list->val);
5147 if (hash->nNumOfElements > 1) {
5148 if (behavior == INTERSECT_NORMAL) {
5149 zend_sort((void *) lists[i], hash->nNumOfElements,
5150 sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5151 (swap_func_t)zend_hash_bucket_swap);
5152 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5153 zend_sort((void *) lists[i], hash->nNumOfElements,
5154 sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5155 (swap_func_t)zend_hash_bucket_swap);
5156 }
5157 }
5158 }
5159
5160 /* copy the argument array if necessary */
5161 in_place = zend_may_modify_arg_in_place(&args[0]);
5162 if (in_place) {
5163 RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5164 } else {
5165 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5166 }
5167
5168 /* go through the lists and look for common values */
5169 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5170 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5171 && key_compare_type == INTERSECT_COMP_KEY_USER) {
5172 BG(user_compare_fci) = *fci_key;
5173 BG(user_compare_fci_cache) = *fci_key_cache;
5174 }
5175
5176 for (i = 1; i < arr_argc; i++) {
5177 if (behavior & INTERSECT_NORMAL) {
5178 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5179 ptrs[i]++;
5180 }
5181 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5182 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5183 ptrs[i]++;
5184 }
5185 if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5186 /* this means that ptrs[i] is not NULL so we can compare
5187 * and "c==0" is from last operation
5188 * in this branch of code we enter only when INTERSECT_ASSOC
5189 * since when we have INTERSECT_KEY compare of data is not wanted. */
5190 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5191 BG(user_compare_fci) = *fci_data;
5192 BG(user_compare_fci_cache) = *fci_data_cache;
5193 }
5194 if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5195 c = 1;
5196 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5197 BG(user_compare_fci) = *fci_key;
5198 BG(user_compare_fci_cache) = *fci_key_cache;
5199 /* When KEY_USER, the last parameter is always the callback */
5200 }
5201 /* we are going to the break */
5202 } else {
5203 /* continue looping */
5204 }
5205 }
5206 }
5207 if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5208 /* delete any values corresponding to remains of ptrs[0] */
5209 /* and exit because they do not present in at least one of */
5210 /* the other arguments */
5211 for (;;) {
5212 p = ptrs[0]++;
5213 if (Z_TYPE(p->val) == IS_UNDEF) {
5214 goto out;
5215 }
5216 if (p->key == NULL) {
5217 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5218 } else {
5219 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5220 }
5221 }
5222 }
5223 if (c) /* here we get if not all are equal */
5224 break;
5225 ptrs[i]++;
5226 }
5227 if (c) {
5228 /* Value of ptrs[0] not in all arguments, delete all entries */
5229 /* with value < value of ptrs[i] */
5230 for (;;) {
5231 p = ptrs[0];
5232 if (p->key == NULL) {
5233 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5234 } else {
5235 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5236 }
5237 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5238 goto out;
5239 }
5240 if (behavior == INTERSECT_NORMAL) {
5241 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5242 break;
5243 }
5244 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5245 /* no need of looping because indexes are unique */
5246 break;
5247 }
5248 }
5249 } else {
5250 /* ptrs[0] is present in all the arguments */
5251 /* Skip all entries with same value as ptrs[0] */
5252 for (;;) {
5253 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5254 goto out;
5255 }
5256 if (behavior == INTERSECT_NORMAL) {
5257 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5258 break;
5259 }
5260 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5261 /* no need of looping because indexes are unique */
5262 break;
5263 }
5264 }
5265 }
5266 }
5267 out:
5268 for (i = 0; i < arr_argc; i++) {
5269 hash = Z_ARRVAL(args[i]);
5270 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5271 }
5272
5273 PHP_ARRAY_CMP_FUNC_RESTORE();
5274
5275 efree(ptrs);
5276 efree(lists);
5277
5278 if (in_place) {
5279 Z_ADDREF_P(return_value);
5280 }
5281 }
5282 /* }}} */
5283
5284 /* {{{ 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. */
5285 PHP_FUNCTION(array_intersect_key)
5286 {
5287 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5288 }
5289 /* }}} */
5290
5291 /* {{{ 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. */
5292 PHP_FUNCTION(array_intersect_ukey)
5293 {
5294 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5295 }
5296 /* }}} */
5297
5298 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5299 PHP_FUNCTION(array_intersect)
5300 {
5301 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5302 }
5303 /* }}} */
5304
5305 /* {{{ 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. */
5306 PHP_FUNCTION(array_uintersect)
5307 {
5308 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5309 }
5310 /* }}} */
5311
5312 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5313 PHP_FUNCTION(array_intersect_assoc)
5314 {
5315 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5316 }
5317 /* }}} */
5318
5319 /* {{{ 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. */
5320 PHP_FUNCTION(array_intersect_uassoc)
5321 {
5322 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5323 }
5324 /* }}} */
5325
5326 /* {{{ 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. */
5327 PHP_FUNCTION(array_uintersect_assoc)
5328 {
5329 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5330 }
5331 /* }}} */
5332
5333 /* {{{ 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. */
5334 PHP_FUNCTION(array_uintersect_uassoc)
5335 {
5336 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5337 }
5338 /* }}} */
5339
5340 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5341 {
5342 uint32_t argc, i;
5343 zval *args;
5344 int (*diff_data_compare_func)(zval *, zval *) = NULL;
5345 bool ok;
5346 zval *val, *data;
5347 zend_string *key;
5348 zend_ulong h;
5349
5350 /* Get the argument count */
5351 argc = ZEND_NUM_ARGS();
5352 if (data_compare_type == DIFF_COMP_DATA_USER) {
5353 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5354 RETURN_THROWS();
5355 }
5356 diff_data_compare_func = zval_user_compare;
5357 } else {
5358 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5359 RETURN_THROWS();
5360 }
5361 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5362 diff_data_compare_func = zval_compare;
5363 }
5364 }
5365
5366 for (i = 0; i < argc; i++) {
5367 if (Z_TYPE(args[i]) != IS_ARRAY) {
5368 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5369 RETURN_THROWS();
5370 }
5371 }
5372
5373 array_init(return_value);
5374
5375 /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5376 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5377 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5378 val = Z_REFVAL_P(val);
5379 }
5380 if (key == NULL) {
5381 ok = 1;
5382 for (i = 1; i < argc; i++) {
5383 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5384 (!diff_data_compare_func ||
5385 diff_data_compare_func(val, data) == 0)
5386 ) {
5387 ok = 0;
5388 break;
5389 }
5390 }
5391 if (ok) {
5392 Z_TRY_ADDREF_P(val);
5393 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5394 }
5395 } else {
5396 ok = 1;
5397 for (i = 1; i < argc; i++) {
5398 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5399 (!diff_data_compare_func ||
5400 diff_data_compare_func(val, data) == 0)
5401 ) {
5402 ok = 0;
5403 break;
5404 }
5405 }
5406 if (ok) {
5407 Z_TRY_ADDREF_P(val);
5408 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5409 }
5410 }
5411 } ZEND_HASH_FOREACH_END();
5412 }
5413 /* }}} */
5414
5415 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5416 {
5417 zval *args = NULL;
5418 HashTable *hash;
5419 uint32_t arr_argc, i;
5420 int c;
5421 uint32_t idx;
5422 Bucket **lists, *list, **ptrs, *p;
5423 char *param_spec;
5424 zend_fcall_info fci1, fci2;
5425 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5426 zend_fcall_info *fci_key = NULL, *fci_data;
5427 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5428 PHP_ARRAY_CMP_FUNC_VARS;
5429
5430 bucket_compare_func_t diff_key_compare_func;
5431 bucket_compare_func_t diff_data_compare_func;
5432
5433 if (behavior == DIFF_NORMAL) {
5434 diff_key_compare_func = php_array_key_compare_string_unstable;
5435
5436 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5437 /* array_diff */
5438 param_spec = "+";
5439 diff_data_compare_func = php_array_data_compare_string_unstable;
5440 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5441 /* array_udiff */
5442 param_spec = "+f";
5443 diff_data_compare_func = php_array_user_compare_unstable;
5444 } else {
5445 ZEND_ASSERT(0 && "Invalid data_compare_type");
5446 return;
5447 }
5448
5449 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5450 RETURN_THROWS();
5451 }
5452 fci_data = &fci1;
5453 fci_data_cache = &fci1_cache;
5454
5455 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5456 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5457 * no comparison of the data is done (part of DIFF_ASSOC) */
5458
5459 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5460 /* array_diff_assoc() or array_diff_key() */
5461 param_spec = "+";
5462 diff_key_compare_func = php_array_key_compare_string_unstable;
5463 diff_data_compare_func = php_array_data_compare_string_unstable;
5464 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5465 /* array_udiff_assoc() */
5466 param_spec = "+f";
5467 diff_key_compare_func = php_array_key_compare_string_unstable;
5468 diff_data_compare_func = php_array_user_compare_unstable;
5469 fci_data = &fci1;
5470 fci_data_cache = &fci1_cache;
5471 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5472 /* array_diff_uassoc() or array_diff_ukey() */
5473 param_spec = "+f";
5474 diff_key_compare_func = php_array_user_key_compare_unstable;
5475 diff_data_compare_func = php_array_data_compare_string_unstable;
5476 fci_key = &fci1;
5477 fci_key_cache = &fci1_cache;
5478 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5479 /* array_udiff_uassoc() */
5480 param_spec = "+ff";
5481 diff_key_compare_func = php_array_user_key_compare_unstable;
5482 diff_data_compare_func = php_array_user_compare_unstable;
5483 fci_data = &fci1;
5484 fci_data_cache = &fci1_cache;
5485 fci_key = &fci2;
5486 fci_key_cache = &fci2_cache;
5487 } else {
5488 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5489 return;
5490 }
5491
5492 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5493 RETURN_THROWS();
5494 }
5495
5496 } else {
5497 ZEND_ASSERT(0 && "Invalid behavior");
5498 return;
5499 }
5500
5501 PHP_ARRAY_CMP_FUNC_BACKUP();
5502
5503 /* for each argument, create and sort list with pointers to the hash buckets */
5504 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5505 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5506
5507 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5508 BG(user_compare_fci) = *fci_data;
5509 BG(user_compare_fci_cache) = *fci_data_cache;
5510 } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5511 BG(user_compare_fci) = *fci_key;
5512 BG(user_compare_fci_cache) = *fci_key_cache;
5513 }
5514
5515 for (i = 0; i < arr_argc; i++) {
5516 if (Z_TYPE(args[i]) != IS_ARRAY) {
5517 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5518 arr_argc = i; /* only free up to i - 1 */
5519 goto out;
5520 }
5521 hash = Z_ARRVAL(args[i]);
5522 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5523 lists[i] = list;
5524 ptrs[i] = list;
5525 if (HT_IS_PACKED(hash)) {
5526 zval *zv = hash->arPacked;
5527 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5528 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5529 ZVAL_COPY_VALUE(&list->val, zv);
5530 list->h = idx;
5531 list->key = NULL;
5532 list++;
5533 }
5534 } else {
5535 p = hash->arData;
5536 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5537 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5538 *list++ = *p;
5539 }
5540 }
5541 ZVAL_UNDEF(&list->val);
5542 if (hash->nNumOfElements > 1) {
5543 if (behavior == DIFF_NORMAL) {
5544 zend_sort((void *) lists[i], hash->nNumOfElements,
5545 sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5546 (swap_func_t)zend_hash_bucket_swap);
5547 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5548 zend_sort((void *) lists[i], hash->nNumOfElements,
5549 sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5550 (swap_func_t)zend_hash_bucket_swap);
5551 }
5552 }
5553 }
5554
5555 /* copy the argument array */
5556 RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5557
5558 /* go through the lists and look for values of ptr[0] that are not in the others */
5559 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5560 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5561 &&
5562 key_compare_type == DIFF_COMP_KEY_USER
5563 ) {
5564 BG(user_compare_fci) = *fci_key;
5565 BG(user_compare_fci_cache) = *fci_key_cache;
5566 }
5567 c = 1;
5568 for (i = 1; i < arr_argc; i++) {
5569 Bucket *ptr = ptrs[i];
5570 if (behavior == DIFF_NORMAL) {
5571 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5572 ptrs[i]++;
5573 }
5574 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5575 while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5576 ptr++;
5577 }
5578 }
5579 if (!c) {
5580 if (behavior == DIFF_NORMAL) {
5581 if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5582 ptrs[i]++;
5583 }
5584 break;
5585 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
5586 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5587 * data comparison is not needed - skipped. */
5588 if (Z_TYPE(ptr->val) != IS_UNDEF) {
5589 if (data_compare_type == DIFF_COMP_DATA_USER) {
5590 BG(user_compare_fci) = *fci_data;
5591 BG(user_compare_fci_cache) = *fci_data_cache;
5592 }
5593 if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5594 /* the data is not the same */
5595 c = -1;
5596 if (key_compare_type == DIFF_COMP_KEY_USER) {
5597 BG(user_compare_fci) = *fci_key;
5598 BG(user_compare_fci_cache) = *fci_key_cache;
5599 }
5600 } else {
5601 break;
5602 /* we have found the element in other arrays thus we don't want it
5603 * in the return_value -> delete from there */
5604 }
5605 }
5606 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5607 /* the behavior here differs from INTERSECT_KEY in php_intersect
5608 * since in the "diff" case we have to remove the entry from
5609 * return_value while when doing intersection the entry must not
5610 * be deleted. */
5611 break; /* remove the key */
5612 }
5613 }
5614 }
5615 if (!c) {
5616 /* ptrs[0] in one of the other arguments */
5617 /* delete all entries with value as ptrs[0] */
5618 for (;;) {
5619 p = ptrs[0];
5620 if (p->key == NULL) {
5621 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5622 } else {
5623 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5624 }
5625 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5626 goto out;
5627 }
5628 if (behavior == DIFF_NORMAL) {
5629 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5630 break;
5631 }
5632 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5633 /* in this case no array_key_compare is needed */
5634 break;
5635 }
5636 }
5637 } else {
5638 /* ptrs[0] in none of the other arguments */
5639 /* skip all entries with value as ptrs[0] */
5640 for (;;) {
5641 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5642 goto out;
5643 }
5644 if (behavior == DIFF_NORMAL) {
5645 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5646 break;
5647 }
5648 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5649 /* in this case no array_key_compare is needed */
5650 break;
5651 }
5652 }
5653 }
5654 }
5655 out:
5656 for (i = 0; i < arr_argc; i++) {
5657 hash = Z_ARRVAL(args[i]);
5658 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5659 }
5660
5661 PHP_ARRAY_CMP_FUNC_RESTORE();
5662
5663 efree(ptrs);
5664 efree(lists);
5665 }
5666 /* }}} */
5667
5668 /* {{{ 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. */
5669 PHP_FUNCTION(array_diff_key)
5670 {
5671 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5672 }
5673 /* }}} */
5674
5675 /* {{{ 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. */
5676 PHP_FUNCTION(array_diff_ukey)
5677 {
5678 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5679 }
5680 /* }}} */
5681
5682 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5683 PHP_FUNCTION(array_diff)
5684 {
5685 zval *args;
5686 uint32_t argc, i;
5687 uint32_t num;
5688 HashTable exclude;
5689 zval *value;
5690 zend_string *str, *tmp_str, *key;
5691 zend_long idx;
5692 zval dummy;
5693
5694 ZEND_PARSE_PARAMETERS_START(1, -1)
5695 Z_PARAM_VARIADIC('+', args, argc)
5696 ZEND_PARSE_PARAMETERS_END();
5697
5698 if (Z_TYPE(args[0]) != IS_ARRAY) {
5699 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5700 RETURN_THROWS();
5701 }
5702
5703 num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5704 if (num == 0) {
5705 for (i = 1; i < argc; i++) {
5706 if (Z_TYPE(args[i]) != IS_ARRAY) {
5707 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5708 RETURN_THROWS();
5709 }
5710 }
5711 RETURN_EMPTY_ARRAY();
5712 } else if (num == 1) {
5713 int found = 0;
5714 zend_string *search_str, *tmp_search_str;
5715
5716 value = NULL;
5717 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5718 break;
5719 } ZEND_HASH_FOREACH_END();
5720
5721 if (!value) {
5722 for (i = 1; i < argc; i++) {
5723 if (Z_TYPE(args[i]) != IS_ARRAY) {
5724 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5725 RETURN_THROWS();
5726 }
5727 }
5728 RETURN_EMPTY_ARRAY();
5729 }
5730
5731 search_str = zval_get_tmp_string(value, &tmp_search_str);
5732
5733 for (i = 1; i < argc; i++) {
5734 if (Z_TYPE(args[i]) != IS_ARRAY) {
5735 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5736 RETURN_THROWS();
5737 }
5738 if (!found) {
5739 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5740 str = zval_get_tmp_string(value, &tmp_str);
5741 if (zend_string_equals(search_str, str)) {
5742 zend_tmp_string_release(tmp_str);
5743 found = 1;
5744 break;
5745 }
5746 zend_tmp_string_release(tmp_str);
5747 } ZEND_HASH_FOREACH_END();
5748 }
5749 }
5750
5751 zend_tmp_string_release(tmp_search_str);
5752
5753 if (found) {
5754 RETVAL_EMPTY_ARRAY();
5755 } else {
5756 ZVAL_COPY(return_value, &args[0]);
5757 }
5758 return;
5759 }
5760
5761 /* count number of elements */
5762 num = 0;
5763 for (i = 1; i < argc; i++) {
5764 if (Z_TYPE(args[i]) != IS_ARRAY) {
5765 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5766 RETURN_THROWS();
5767 }
5768 num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5769 }
5770
5771 if (num == 0) {
5772 ZVAL_COPY(return_value, &args[0]);
5773 return;
5774 }
5775
5776 ZVAL_NULL(&dummy);
5777 /* create exclude map */
5778 zend_hash_init(&exclude, num, NULL, NULL, 0);
5779 for (i = 1; i < argc; i++) {
5780 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5781 str = zval_get_tmp_string(value, &tmp_str);
5782 zend_hash_add(&exclude, str, &dummy);
5783 zend_tmp_string_release(tmp_str);
5784 } ZEND_HASH_FOREACH_END();
5785 }
5786
5787 /* copy all elements of first array that are not in exclude set */
5788 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5789 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5790 str = zval_get_tmp_string(value, &tmp_str);
5791 if (!zend_hash_exists(&exclude, str)) {
5792 if (key) {
5793 value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5794 } else {
5795 value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5796 }
5797 zval_add_ref(value);
5798 }
5799 zend_tmp_string_release(tmp_str);
5800 } ZEND_HASH_FOREACH_END();
5801
5802 zend_hash_destroy(&exclude);
5803 }
5804 /* }}} */
5805
5806 /* {{{ 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. */
5807 PHP_FUNCTION(array_udiff)
5808 {
5809 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5810 }
5811 /* }}} */
5812
5813 /* {{{ 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 */
5814 PHP_FUNCTION(array_diff_assoc)
5815 {
5816 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5817 }
5818 /* }}} */
5819
5820 /* {{{ 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. */
5821 PHP_FUNCTION(array_diff_uassoc)
5822 {
5823 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5824 }
5825 /* }}} */
5826
5827 /* {{{ 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. */
5828 PHP_FUNCTION(array_udiff_assoc)
5829 {
5830 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5831 }
5832 /* }}} */
5833
5834 /* {{{ 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. */
5835 PHP_FUNCTION(array_udiff_uassoc)
5836 {
5837 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5838 }
5839 /* }}} */
5840
5841 #define MULTISORT_ORDER 0
5842 #define MULTISORT_TYPE 1
5843 #define MULTISORT_LAST 2
5844
5845 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5846 {
5847 Bucket *ab = *(Bucket **)a;
5848 Bucket *bb = *(Bucket **)b;
5849 int r;
5850 zend_long result;
5851
5852 r = 0;
5853 do {
5854 result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5855 if (result != 0) {
5856 return result > 0 ? 1 : -1;
5857 }
5858 r++;
5859 } while (Z_TYPE(ab[r].val) != IS_UNDEF);
5860
5861 return stable_sort_fallback(&ab[r], &bb[r]);
5862 }
5863 /* }}} */
5864
5865 #define MULTISORT_ABORT \
5866 efree(func); \
5867 efree(arrays); \
5868 return;
5869
5870 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5871 Bucket *t;
5872 Bucket **f = (Bucket **)p;
5873 Bucket **g = (Bucket **)q;
5874
5875 t = *f;
5876 *f = *g;
5877 *g = t;
5878 }
5879 /* }}} */
5880
5881 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5882 PHP_FUNCTION(array_multisort)
5883 {
5884 zval* args;
5885 zval** arrays;
5886 Bucket** indirect;
5887 uint32_t idx;
5888 HashTable* hash;
5889 uint32_t argc;
5890 uint32_t array_size;
5891 uint32_t num_arrays = 0;
5892 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
5893 int sort_order = PHP_SORT_ASC;
5894 int sort_type = PHP_SORT_REGULAR;
5895 uint32_t i, k, n;
5896 bucket_compare_func_t *func;
5897
5898 ZEND_PARSE_PARAMETERS_START(1, -1)
5899 Z_PARAM_VARIADIC('+', args, argc)
5900 ZEND_PARSE_PARAMETERS_END();
5901
5902 /* Allocate space for storing pointers to input arrays and sort flags. */
5903 arrays = (zval **)ecalloc(argc, sizeof(zval *));
5904 for (i = 0; i < MULTISORT_LAST; i++) {
5905 parse_state[i] = 0;
5906 }
5907 func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
5908
5909 /* Here we go through the input arguments and parse them. Each one can
5910 * be either an array or a sort flag which follows an array. If not
5911 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5912 * accordingly. There can't be two sort flags of the same type after an
5913 * array, and the very first argument has to be an array. */
5914 for (i = 0; i < argc; i++) {
5915 zval *arg = &args[i];
5916
5917 ZVAL_DEREF(arg);
5918 if (Z_TYPE_P(arg) == IS_ARRAY) {
5919 SEPARATE_ARRAY(arg);
5920 /* We see the next array, so we update the sort flags of
5921 * the previous array and reset the sort flags. */
5922 if (i > 0) {
5923 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5924 sort_order = PHP_SORT_ASC;
5925 sort_type = PHP_SORT_REGULAR;
5926 }
5927 arrays[num_arrays++] = arg;
5928
5929 /* Next one may be an array or a list of sort flags. */
5930 for (k = 0; k < MULTISORT_LAST; k++) {
5931 parse_state[k] = 1;
5932 }
5933 } else if (Z_TYPE_P(arg) == IS_LONG) {
5934 switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5935 case PHP_SORT_ASC:
5936 case PHP_SORT_DESC:
5937 /* flag allowed here */
5938 if (parse_state[MULTISORT_ORDER] == 1) {
5939 /* Save the flag and make sure then next arg is not the current flag. */
5940 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5941 parse_state[MULTISORT_ORDER] = 0;
5942 } else {
5943 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5944 MULTISORT_ABORT;
5945 }
5946 break;
5947
5948 case PHP_SORT_REGULAR:
5949 case PHP_SORT_NUMERIC:
5950 case PHP_SORT_STRING:
5951 case PHP_SORT_NATURAL:
5952 case PHP_SORT_LOCALE_STRING:
5953 /* flag allowed here */
5954 if (parse_state[MULTISORT_TYPE] == 1) {
5955 /* Save the flag and make sure then next arg is not the current flag. */
5956 sort_type = (int)Z_LVAL_P(arg);
5957 parse_state[MULTISORT_TYPE] = 0;
5958 } else {
5959 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5960 MULTISORT_ABORT;
5961 }
5962 break;
5963
5964 default:
5965 zend_argument_value_error(i + 1, "must be a valid sort flag");
5966 MULTISORT_ABORT;
5967 break;
5968
5969 }
5970 } else {
5971 zend_argument_type_error(i + 1, "must be an array or a sort flag");
5972 MULTISORT_ABORT;
5973 }
5974 }
5975 /* Take care of the last array sort flags. */
5976 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5977
5978 /* Make sure the arrays are of the same size. */
5979 array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
5980 for (i = 1; i < num_arrays; i++) {
5981 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
5982 zend_value_error("Array sizes are inconsistent");
5983 MULTISORT_ABORT;
5984 }
5985 }
5986
5987 /* If all arrays are empty we don't need to do anything. */
5988 if (array_size < 1) {
5989 efree(func);
5990 efree(arrays);
5991 RETURN_TRUE;
5992 }
5993
5994 /* Create the indirection array. This array is of size MxN, where
5995 * M is the number of entries in each input array and N is the number
5996 * of the input arrays + 1. The last column is UNDEF to indicate the end
5997 * of the row. It also stores the original position for stable sorting. */
5998 indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
5999 /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6000 Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6001 for (i = 0; i < array_size; i++) {
6002 indirect[i] = indirects + (i * (num_arrays + 1));
6003 }
6004 for (i = 0; i < num_arrays; i++) {
6005 k = 0;
6006 if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6007 zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6008 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6009 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6010 ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6011 indirect[k][i].h = idx;
6012 indirect[k][i].key = NULL;
6013 k++;
6014 }
6015 } else {
6016 Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6017 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6018 if (Z_TYPE(p->val) == IS_UNDEF) continue;
6019 indirect[k][i] = *p;
6020 k++;
6021 }
6022 }
6023 }
6024 for (k = 0; k < array_size; k++) {
6025 ZVAL_UNDEF(&indirect[k][num_arrays].val);
6026 Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6027 }
6028
6029 /* Do the actual sort magic - bada-bim, bada-boom. */
6030 zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6031 if (EG(exception)) {
6032 goto clean_up;
6033 }
6034
6035 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6036 for (i = 0; i < num_arrays; i++) {
6037 hash = Z_ARRVAL_P(arrays[i]);
6038 hash->nNumUsed = array_size;
6039 hash->nNextFreeElement = array_size;
6040 hash->nInternalPointer = 0;
6041 if (HT_IS_PACKED(hash)) {
6042 for (k = 0; k < array_size; k++) {
6043 ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6044 }
6045 } else {
6046 bool repack = true;
6047
6048 for (n = 0, k = 0; k < array_size; k++) {
6049 hash->arData[k] = indirect[k][i];
6050 if (hash->arData[k].key == NULL) {
6051 hash->arData[k].h = n++;
6052 } else {
6053 repack = false;
6054 }
6055 }
6056 if (repack) {
6057 zend_hash_to_packed(hash);
6058 } else {
6059 zend_hash_rehash(hash);
6060 }
6061 }
6062 }
6063 RETVAL_TRUE;
6064
6065 clean_up:
6066 efree(indirects);
6067 efree(indirect);
6068 efree(func);
6069 efree(arrays);
6070 }
6071 /* }}} */
6072
6073 /* {{{ php_array_pick_keys */
6074 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)
6075 {
6076 HashTable *ht = Z_ARRVAL_P(input);
6077 uint32_t num_avail = zend_hash_num_elements(ht);
6078 zend_long i, randval;
6079 zend_string *string_key;
6080 zend_ulong num_key;
6081 zval *zv;
6082 Bucket *b;
6083 zend_bitset bitset;
6084 int negative_bitset = 0;
6085 uint32_t bitset_len;
6086 ALLOCA_FLAG(use_heap);
6087
6088 if (num_avail == 0) {
6089 if (!silent) {
6090 zend_argument_value_error(1, "cannot be empty");
6091 }
6092 return false;
6093 }
6094
6095 if (num_req == 1) {
6096 if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6097 /* If less than 1/2 of elements are used, don't sample. Instead search for a
6098 * specific offset using linear scan. */
6099 i = 0;
6100 randval = algo->range(status, 0, num_avail - 1);
6101 if (EG(exception)) {
6102 return false;
6103 }
6104 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6105 if (i == randval) {
6106 if (string_key) {
6107 ZVAL_STR_COPY(retval, string_key);
6108 } else {
6109 ZVAL_LONG(retval, num_key);
6110 }
6111 return true;
6112 }
6113 i++;
6114 } ZEND_HASH_FOREACH_END();
6115 }
6116
6117 /* Sample random buckets until we hit one that is not empty.
6118 * The worst case probability of hitting an empty element is 1-1/2. The worst case
6119 * probability of hitting N empty elements in a row is (1-1/2)**N.
6120 * For N=10 this becomes smaller than 0.1%. */
6121 if (HT_IS_PACKED(ht)) {
6122 do {
6123 randval = algo->range(status, 0, ht->nNumUsed - 1);
6124 if (EG(exception)) {
6125 return false;
6126 }
6127 zv = &ht->arPacked[randval];
6128 if (!Z_ISUNDEF_P(zv)) {
6129 ZVAL_LONG(retval, randval);
6130 return true;
6131 }
6132 } while (true);
6133 } else {
6134 do {
6135 randval = algo->range(status, 0, ht->nNumUsed - 1);
6136 if (EG(exception)) {
6137 return false;
6138 }
6139 b = &ht->arData[randval];
6140 if (!Z_ISUNDEF(b->val)) {
6141 if (b->key) {
6142 ZVAL_STR_COPY(retval, b->key);
6143 } else {
6144 ZVAL_LONG(retval, b->h);
6145 }
6146 return true;
6147 }
6148 } while (true);
6149 }
6150 }
6151
6152 if (num_req <= 0 || num_req > num_avail) {
6153 if (!silent) {
6154 zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6155 }
6156 return false;
6157 }
6158
6159 /* Make the return value an array only if we need to pass back more than one result. */
6160 array_init_size(retval, (uint32_t) num_req);
6161 if (num_req > (num_avail >> 1)) {
6162 negative_bitset = 1;
6163 num_req = num_avail - num_req;
6164 }
6165
6166 bitset_len = zend_bitset_len(num_avail);
6167 bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6168 zend_bitset_clear(bitset, bitset_len);
6169
6170 i = num_req;
6171 int failures = 0;
6172 while (i) {
6173 randval = algo->range(status, 0, num_avail - 1);
6174 if (EG(exception)) {
6175 goto fail;
6176 }
6177 if (zend_bitset_in(bitset, randval)) {
6178 if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6179 if (!silent) {
6180 zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6181 }
6182
6183 goto fail;
6184 }
6185 } else {
6186 zend_bitset_incl(bitset, randval);
6187 i--;
6188 failures = 0;
6189 }
6190 }
6191
6192 zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6193 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6194 /* We can't use zend_hash_index_find()
6195 * because the array may have string keys or gaps. */
6196 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6197 if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6198 if (string_key) {
6199 ZEND_HASH_FILL_SET_STR_COPY(string_key);
6200 } else {
6201 ZEND_HASH_FILL_SET_LONG(num_key);
6202 }
6203 ZEND_HASH_FILL_NEXT();
6204 }
6205 i++;
6206 } ZEND_HASH_FOREACH_END();
6207 } ZEND_HASH_FILL_END();
6208
6209 free_alloca(bitset, use_heap);
6210
6211 return true;
6212
6213 fail:
6214 free_alloca(bitset, use_heap);
6215
6216 return false;
6217 }
6218 /* }}} */
6219
6220 /* {{{ Return key/keys for random entry/entries in the array */
6221 PHP_FUNCTION(array_rand)
6222 {
6223 zval *input;
6224 zend_long num_req = 1;
6225
6226 ZEND_PARSE_PARAMETERS_START(1, 2)
6227 Z_PARAM_ARRAY(input)
6228 Z_PARAM_OPTIONAL
6229 Z_PARAM_LONG(num_req)
6230 ZEND_PARSE_PARAMETERS_END();
6231
6232 if (!php_array_pick_keys(
6233 php_random_default_algo(),
6234 php_random_default_status(),
6235 input,
6236 num_req,
6237 return_value,
6238 false)
6239 ) {
6240 RETURN_THROWS();
6241 }
6242 }
6243 /* }}} */
6244
6245 /* Wrapper for array_sum and array_product */
6246 static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6247 {
6248 HashTable *input;
6249 zval *entry;
6250
6251 ZEND_PARSE_PARAMETERS_START(1, 1)
6252 Z_PARAM_ARRAY_HT(input)
6253 ZEND_PARSE_PARAMETERS_END();
6254
6255 if (zend_hash_num_elements(input) == 0) {
6256 RETURN_LONG(initial);
6257 }
6258
6259 ZVAL_LONG(return_value, initial);
6260 ZEND_HASH_FOREACH_VAL(input, entry) {
6261 /* For objects we try to cast them to a numeric type */
6262 if (Z_TYPE_P(entry) == IS_OBJECT) {
6263 zval dst;
6264 zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6265
6266 /* Do not type error for BC */
6267 if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6268 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6269 op_name, zend_zval_type_name(entry));
6270 continue;
6271 }
6272 op(return_value, return_value, &dst);
6273 continue;
6274 }
6275
6276 zend_result status = op(return_value, return_value, entry);
6277 if (status == FAILURE) {
6278 ZEND_ASSERT(EG(exception));
6279 zend_clear_exception();
6280 /* BC resources: previously resources were cast to int */
6281 if (Z_TYPE_P(entry) == IS_RESOURCE) {
6282 zval tmp;
6283 ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6284 op(return_value, return_value, &tmp);
6285 }
6286 /* BC non numeric strings: previously were cast to 0 */
6287 else if (Z_TYPE_P(entry) == IS_STRING) {
6288 zval tmp;
6289 ZVAL_LONG(&tmp, 0);
6290 op(return_value, return_value, &tmp);
6291 }
6292 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6293 op_name, zend_zval_type_name(entry));
6294 }
6295 } ZEND_HASH_FOREACH_END();
6296 }
6297
6298 /* {{{ Returns the sum of the array entries */
6299 PHP_FUNCTION(array_sum)
6300 {
6301 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6302 }
6303 /* }}} */
6304
6305 /* {{{ Returns the product of the array entries */
6306 PHP_FUNCTION(array_product)
6307 {
6308 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6309 }
6310 /* }}} */
6311
6312 /* {{{ Iteratively reduce the array to a single value via the callback. */
6313 PHP_FUNCTION(array_reduce)
6314 {
6315 zval *input;
6316 zval args[2];
6317 zval *operand;
6318 zval retval;
6319 zend_fcall_info fci;
6320 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6321 zval *initial = NULL;
6322 HashTable *htbl;
6323
6324 ZEND_PARSE_PARAMETERS_START(2, 3)
6325 Z_PARAM_ARRAY(input)
6326 Z_PARAM_FUNC(fci, fci_cache)
6327 Z_PARAM_OPTIONAL
6328 Z_PARAM_ZVAL(initial)
6329 ZEND_PARSE_PARAMETERS_END();
6330
6331
6332 if (ZEND_NUM_ARGS() > 2) {
6333 ZVAL_COPY(return_value, initial);
6334 } else {
6335 ZVAL_NULL(return_value);
6336 }
6337
6338 /* (zval **)input points to an element of argument stack
6339 * the base pointer of which is subject to change.
6340 * thus we need to keep the pointer to the hashtable for safety */
6341 htbl = Z_ARRVAL_P(input);
6342
6343 if (zend_hash_num_elements(htbl) == 0) {
6344 return;
6345 }
6346
6347 fci.retval = &retval;
6348 fci.param_count = 2;
6349
6350 ZEND_HASH_FOREACH_VAL(htbl, operand) {
6351 ZVAL_COPY_VALUE(&args[0], return_value);
6352 ZVAL_COPY(&args[1], operand);
6353 fci.params = args;
6354
6355 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6356 zval_ptr_dtor(&args[1]);
6357 zval_ptr_dtor(&args[0]);
6358 ZVAL_COPY_VALUE(return_value, &retval);
6359 if (UNEXPECTED(Z_ISREF_P(return_value))) {
6360 zend_unwrap_reference(return_value);
6361 }
6362 } else {
6363 zval_ptr_dtor(&args[1]);
6364 zval_ptr_dtor(&args[0]);
6365 RETURN_NULL();
6366 }
6367 } ZEND_HASH_FOREACH_END();
6368 }
6369 /* }}} */
6370
6371 /* {{{ Filters elements from the array via the callback. */
6372 PHP_FUNCTION(array_filter)
6373 {
6374 zval *array;
6375 zval *operand;
6376 zval *key;
6377 zval args[2];
6378 zval retval;
6379 bool have_callback = 0;
6380 zend_long use_type = 0;
6381 zend_string *string_key;
6382 zend_fcall_info fci = empty_fcall_info;
6383 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6384 zend_ulong num_key;
6385
6386 ZEND_PARSE_PARAMETERS_START(1, 3)
6387 Z_PARAM_ARRAY(array)
6388 Z_PARAM_OPTIONAL
6389 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6390 Z_PARAM_LONG(use_type)
6391 ZEND_PARSE_PARAMETERS_END();
6392
6393 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6394 RETVAL_EMPTY_ARRAY();
6395 return;
6396 }
6397 array_init(return_value);
6398
6399 if (ZEND_FCI_INITIALIZED(fci)) {
6400 have_callback = 1;
6401 fci.retval = &retval;
6402 if (use_type == ARRAY_FILTER_USE_BOTH) {
6403 fci.param_count = 2;
6404 key = &args[1];
6405 } else {
6406 fci.param_count = 1;
6407 key = &args[0];
6408 }
6409 }
6410
6411 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6412 if (have_callback) {
6413 if (use_type) {
6414 /* Set up the key */
6415 if (!string_key) {
6416 ZVAL_LONG(key, num_key);
6417 } else {
6418 ZVAL_STR_COPY(key, string_key);
6419 }
6420 }
6421 if (use_type != ARRAY_FILTER_USE_KEY) {
6422 ZVAL_COPY(&args[0], operand);
6423 }
6424 fci.params = args;
6425
6426 if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6427 int retval_true;
6428
6429 zval_ptr_dtor(&args[0]);
6430 if (use_type == ARRAY_FILTER_USE_BOTH) {
6431 zval_ptr_dtor(&args[1]);
6432 }
6433 retval_true = zend_is_true(&retval);
6434 zval_ptr_dtor(&retval);
6435 if (!retval_true) {
6436 continue;
6437 }
6438 } else {
6439 zval_ptr_dtor(&args[0]);
6440 if (use_type == ARRAY_FILTER_USE_BOTH) {
6441 zval_ptr_dtor(&args[1]);
6442 }
6443 return;
6444 }
6445 } else if (!zend_is_true(operand)) {
6446 continue;
6447 }
6448
6449 if (string_key) {
6450 operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6451 } else {
6452 operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6453 }
6454 zval_add_ref(operand);
6455 } ZEND_HASH_FOREACH_END();
6456 }
6457 /* }}} */
6458
6459 /* {{{ Applies the callback to the elements in given arrays. */
6460 PHP_FUNCTION(array_map)
6461 {
6462 zval *arrays = NULL;
6463 int n_arrays = 0;
6464 zval result;
6465 zend_fcall_info fci = empty_fcall_info;
6466 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6467 int i;
6468 uint32_t k, maxlen = 0;
6469
6470 ZEND_PARSE_PARAMETERS_START(2, -1)
6471 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6472 Z_PARAM_VARIADIC('+', arrays, n_arrays)
6473 ZEND_PARSE_PARAMETERS_END();
6474
6475 if (n_arrays == 1) {
6476 zend_ulong num_key;
6477 zend_string *str_key;
6478 zval *zv, arg;
6479 int ret;
6480
6481 if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6482 zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6483 RETURN_THROWS();
6484 }
6485 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6486
6487 /* Short-circuit: if no callback and only one array, just return it. */
6488 if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6489 ZVAL_COPY(return_value, &arrays[0]);
6490 return;
6491 }
6492
6493 array_init_size(return_value, maxlen);
6494 zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6495
6496 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6497 fci.retval = &result;
6498 fci.param_count = 1;
6499 fci.params = &arg;
6500
6501 ZVAL_COPY(&arg, zv);
6502 ret = zend_call_function(&fci, &fci_cache);
6503 i_zval_ptr_dtor(&arg);
6504 if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6505 zend_array_destroy(Z_ARR_P(return_value));
6506 RETURN_NULL();
6507 }
6508 if (str_key) {
6509 _zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6510 } else {
6511 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6512 }
6513 } ZEND_HASH_FOREACH_END();
6514 } else {
6515 uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6516
6517 for (i = 0; i < n_arrays; i++) {
6518 if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6519 zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6520 efree(array_pos);
6521 RETURN_THROWS();
6522 }
6523 if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6524 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6525 }
6526 }
6527
6528 array_init_size(return_value, maxlen);
6529
6530 if (!ZEND_FCI_INITIALIZED(fci)) {
6531 zval zv;
6532
6533 /* We iterate through all the arrays at once. */
6534 for (k = 0; k < maxlen; k++) {
6535
6536 /* If no callback, the result will be an array, consisting of current
6537 * entries from all arrays. */
6538 array_init_size(&result, n_arrays);
6539
6540 for (i = 0; i < n_arrays; i++) {
6541 /* If this array still has elements, add the current one to the
6542 * parameter list, otherwise use null value. */
6543 uint32_t pos = array_pos[i];
6544 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6545 while (1) {
6546 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6547 ZVAL_NULL(&zv);
6548 break;
6549 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6550 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6551 array_pos[i] = pos + 1;
6552 break;
6553 }
6554 pos++;
6555 }
6556 } else {
6557 while (1) {
6558 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6559 ZVAL_NULL(&zv);
6560 break;
6561 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6562 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6563 array_pos[i] = pos + 1;
6564 break;
6565 }
6566 pos++;
6567 }
6568 }
6569 zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6570 }
6571
6572 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6573 }
6574 } else {
6575 zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6576
6577 /* We iterate through all the arrays at once. */
6578 for (k = 0; k < maxlen; k++) {
6579 for (i = 0; i < n_arrays; i++) {
6580 /* If this array still has elements, add the current one to the
6581 * parameter list, otherwise use null value. */
6582 uint32_t pos = array_pos[i];
6583 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6584 while (1) {
6585 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6586 ZVAL_NULL(¶ms[i]);
6587 break;
6588 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6589 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6590 array_pos[i] = pos + 1;
6591 break;
6592 }
6593 pos++;
6594 }
6595 } else {
6596 while (1) {
6597 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6598 ZVAL_NULL(¶ms[i]);
6599 break;
6600 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6601 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6602 array_pos[i] = pos + 1;
6603 break;
6604 }
6605 pos++;
6606 }
6607 }
6608 }
6609
6610 fci.retval = &result;
6611 fci.param_count = n_arrays;
6612 fci.params = params;
6613
6614 if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6615 efree(array_pos);
6616 zend_array_destroy(Z_ARR_P(return_value));
6617 for (i = 0; i < n_arrays; i++) {
6618 zval_ptr_dtor(¶ms[i]);
6619 }
6620 efree(params);
6621 RETURN_NULL();
6622 } else {
6623 for (i = 0; i < n_arrays; i++) {
6624 zval_ptr_dtor(¶ms[i]);
6625 }
6626 }
6627
6628 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6629 }
6630
6631 efree(params);
6632 }
6633 efree(array_pos);
6634 }
6635 }
6636 /* }}} */
6637
6638 /* {{{ Checks if the given key or index exists in the array */
6639 PHP_FUNCTION(array_key_exists)
6640 {
6641 zval *key;
6642 HashTable *ht;
6643
6644 ZEND_PARSE_PARAMETERS_START(2, 2)
6645 Z_PARAM_ZVAL(key)
6646 Z_PARAM_ARRAY_HT(ht)
6647 ZEND_PARSE_PARAMETERS_END();
6648
6649 switch (Z_TYPE_P(key)) {
6650 case IS_STRING:
6651 RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6652 break;
6653 case IS_LONG:
6654 RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6655 break;
6656 case IS_NULL:
6657 RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6658 break;
6659 case IS_DOUBLE:
6660 RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6661 break;
6662 case IS_FALSE:
6663 RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6664 break;
6665 case IS_TRUE:
6666 RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6667 break;
6668 case IS_RESOURCE:
6669 zend_use_resource_as_offset(key);
6670 RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6671 break;
6672 default:
6673 zend_argument_type_error(1, "must be a valid array offset type");
6674 break;
6675 }
6676 }
6677 /* }}} */
6678
6679 /* {{{ Split array into chunks */
6680 PHP_FUNCTION(array_chunk)
6681 {
6682 int num_in;
6683 zend_long size, current = 0;
6684 zend_string *str_key;
6685 zend_ulong num_key;
6686 bool preserve_keys = 0;
6687 zval *input = NULL;
6688 zval chunk;
6689 zval *entry;
6690
6691 ZEND_PARSE_PARAMETERS_START(2, 3)
6692 Z_PARAM_ARRAY(input)
6693 Z_PARAM_LONG(size)
6694 Z_PARAM_OPTIONAL
6695 Z_PARAM_BOOL(preserve_keys)
6696 ZEND_PARSE_PARAMETERS_END();
6697
6698 /* Do bounds checking for size parameter. */
6699 if (size < 1) {
6700 zend_argument_value_error(2, "must be greater than 0");
6701 RETURN_THROWS();
6702 }
6703
6704 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6705
6706 if (size > num_in) {
6707 if (num_in == 0) {
6708 RETVAL_EMPTY_ARRAY();
6709 return;
6710 }
6711 size = num_in;
6712 }
6713
6714 array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6715
6716 ZVAL_UNDEF(&chunk);
6717
6718 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6719 /* If new chunk, create and initialize it. */
6720 if (Z_TYPE(chunk) == IS_UNDEF) {
6721 array_init_size(&chunk, (uint32_t)size);
6722 }
6723
6724 /* Add entry to the chunk, preserving keys if necessary. */
6725 if (preserve_keys) {
6726 if (str_key) {
6727 entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6728 } else {
6729 entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6730 }
6731 } else {
6732 entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6733 }
6734 zval_add_ref(entry);
6735
6736 /* If reached the chunk size, add it to the result array, and reset the
6737 * pointer. */
6738 if (!(++current % size)) {
6739 add_next_index_zval(return_value, &chunk);
6740 ZVAL_UNDEF(&chunk);
6741 }
6742 } ZEND_HASH_FOREACH_END();
6743
6744 /* Add the final chunk if there is one. */
6745 if (Z_TYPE(chunk) != IS_UNDEF) {
6746 add_next_index_zval(return_value, &chunk);
6747 }
6748 }
6749 /* }}} */
6750
6751 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6752 PHP_FUNCTION(array_combine)
6753 {
6754 HashTable *values, *keys;
6755 uint32_t pos_values = 0;
6756 zval *entry_keys, *entry_values;
6757 int num_keys, num_values;
6758
6759 ZEND_PARSE_PARAMETERS_START(2, 2)
6760 Z_PARAM_ARRAY_HT(keys)
6761 Z_PARAM_ARRAY_HT(values)
6762 ZEND_PARSE_PARAMETERS_END();
6763
6764 num_keys = zend_hash_num_elements(keys);
6765 num_values = zend_hash_num_elements(values);
6766
6767 if (num_keys != num_values) {
6768 zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6769 RETURN_THROWS();
6770 }
6771
6772 if (!num_keys) {
6773 RETURN_EMPTY_ARRAY();
6774 }
6775
6776 array_init_size(return_value, num_keys);
6777 ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6778 while (1) {
6779 if (pos_values >= values->nNumUsed) {
6780 break;
6781 }
6782 entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6783 if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6784 if (Z_TYPE_P(entry_keys) == IS_LONG) {
6785 entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6786 Z_LVAL_P(entry_keys), entry_values);
6787 } else {
6788 zend_string *tmp_key;
6789 zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6790 entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6791 key, entry_values);
6792 zend_tmp_string_release(tmp_key);
6793 }
6794 zval_add_ref(entry_values);
6795 pos_values++;
6796 break;
6797 }
6798 pos_values++;
6799 }
6800 } ZEND_HASH_FOREACH_END();
6801 }
6802 /* }}} */
6803