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