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