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