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