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