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(const php_random_algo *algo, php_random_status *status, zval *array) /* {{{ */
3215 {
3216 int64_t idx, j, n_elems, rnd_idx, n_left;
3217 zval *zv, temp;
3218 HashTable *hash;
3219
3220 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3221
3222 if (n_elems < 1) {
3223 return true;
3224 }
3225
3226 hash = Z_ARRVAL_P(array);
3227 n_left = n_elems;
3228
3229 if (!HT_IS_PACKED(hash)) {
3230 if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3231 Bucket *p = hash->arData;
3232 zend_long i = hash->nNumUsed;
3233
3234 for (; i > 0; p++, i--) {
3235 if (p->key) {
3236 zend_string_release(p->key);
3237 p->key = NULL;
3238 }
3239 }
3240 }
3241 zend_hash_to_packed(hash);
3242 }
3243
3244 if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3245 if (hash->nNumUsed != hash->nNumOfElements) {
3246 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3247 zv = hash->arPacked + idx;
3248 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3249 if (j != idx) {
3250 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3251 }
3252 j++;
3253 }
3254 }
3255 while (--n_left) {
3256 rnd_idx = algo->range(status, 0, n_left);
3257 if (EG(exception)) {
3258 return false;
3259 }
3260 if (rnd_idx != n_left) {
3261 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3262 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3263 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3264 }
3265 }
3266 } else {
3267 zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3268
3269 if (hash->nNumUsed != hash->nNumOfElements) {
3270 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3271 zv = hash->arPacked + idx;
3272 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3273 if (j != idx) {
3274 ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3275 if (idx == iter_pos) {
3276 zend_hash_iterators_update(hash, idx, j);
3277 iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3278 }
3279 }
3280 j++;
3281 }
3282 }
3283 while (--n_left) {
3284 rnd_idx = algo->range(status, 0, n_left);
3285 if (EG(exception)) {
3286 return false;
3287 }
3288 if (rnd_idx != n_left) {
3289 ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3290 ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3291 ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3292 zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
3293 }
3294 }
3295 }
3296 hash->nNumUsed = n_elems;
3297 hash->nInternalPointer = 0;
3298 hash->nNextFreeElement = n_elems;
3299
3300 return true;
3301 }
3302 /* }}} */
3303
3304 /* {{{ Randomly shuffle the contents of an array */
3305 PHP_FUNCTION(shuffle)
3306 {
3307 zval *array;
3308
3309 ZEND_PARSE_PARAMETERS_START(1, 1)
3310 Z_PARAM_ARRAY_EX(array, 0, 1)
3311 ZEND_PARSE_PARAMETERS_END();
3312
3313 php_array_data_shuffle(php_random_default_algo(), php_random_default_status(), array);
3314
3315 RETURN_TRUE;
3316 }
3317 /* }}} */
3318
3319 static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
3320 {
3321 HashTable out_hash; /* Output hashtable */
3322 zend_long num_in; /* Number of entries in the input hashtable */
3323 zend_long pos; /* Current position in the hashtable */
3324 uint32_t idx;
3325 zval *entry; /* Hash entry */
3326 uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3327
3328 /* Get number of entries in the input hash */
3329 num_in = zend_hash_num_elements(in_hash);
3330
3331 /* Clamp the offset.. */
3332 if (offset > num_in) {
3333 offset = num_in;
3334 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3335 offset = 0;
3336 }
3337
3338 /* ..and the length */
3339 if (length < 0) {
3340 length = num_in - offset + length;
3341 } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3342 length = num_in - offset;
3343 }
3344
3345 /* Create and initialize output hash */
3346 zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
3347
3348 if (HT_IS_PACKED(in_hash)) {
3349 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3350 entry = in_hash->arPacked;
3351 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3352 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3353
3354 zend_hash_next_index_insert_new(&out_hash, entry);
3355 if (idx == iter_pos) {
3356 if ((zend_long)idx != pos) {
3357 zend_hash_iterators_update(in_hash, idx, pos);
3358 }
3359 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3360 }
3361 pos++;
3362 }
3363
3364 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3365 if (removed != NULL) {
3366 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3367 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3368 pos++;
3369 Z_TRY_ADDREF_P(entry);
3370 zend_hash_next_index_insert_new(removed, entry);
3371 zend_hash_packed_del_val(in_hash, entry);
3372 /* Bump iterator positions to the element after replacement. */
3373 if (idx == iter_pos) {
3374 zend_hash_iterators_update(in_hash, idx, offset + length);
3375 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3376 }
3377 }
3378 } else { /* otherwise just skip those entries */
3379 int pos2 = pos;
3380
3381 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++, entry++) {
3382 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3383 pos2++;
3384 zend_hash_packed_del_val(in_hash, entry);
3385 /* Bump iterator positions to the element after replacement. */
3386 if (idx == iter_pos) {
3387 zend_hash_iterators_update(in_hash, idx, offset + length);
3388 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3389 }
3390 }
3391 }
3392
3393 /* If there are entries to insert.. */
3394 if (replace) {
3395 ZEND_HASH_FOREACH_VAL(replace, entry) {
3396 Z_TRY_ADDREF_P(entry);
3397 zend_hash_next_index_insert_new(&out_hash, entry);
3398 pos++;
3399 } ZEND_HASH_FOREACH_END();
3400 }
3401
3402 /* Copy the remaining input hash entries to the output hash */
3403 entry = in_hash->arPacked + idx;
3404 for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3405 if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3406 zend_hash_next_index_insert_new(&out_hash, entry);
3407 if (idx == iter_pos) {
3408 if ((zend_long)idx != pos) {
3409 zend_hash_iterators_update(in_hash, idx, pos);
3410 }
3411 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3412 }
3413 pos++;
3414 }
3415 } else {
3416 Bucket *p = in_hash->arData;
3417
3418 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3419 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, p++) {
3420 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3421 entry = &p->val;
3422
3423 /* Update output hash depending on key type */
3424 if (p->key == NULL) {
3425 zend_hash_next_index_insert_new(&out_hash, entry);
3426 } else {
3427 zend_hash_add_new(&out_hash, p->key, entry);
3428 }
3429 if (idx == iter_pos) {
3430 if ((zend_long)idx != pos) {
3431 zend_hash_iterators_update(in_hash, idx, pos);
3432 }
3433 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3434 }
3435 pos++;
3436 }
3437
3438 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3439 if (removed != NULL) {
3440 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++, p++) {
3441 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3442 pos++;
3443 entry = &p->val;
3444 Z_TRY_ADDREF_P(entry);
3445 if (p->key == NULL) {
3446 zend_hash_next_index_insert_new(removed, entry);
3447 } else {
3448 zend_hash_add_new(removed, p->key, entry);
3449 }
3450 zend_hash_del_bucket(in_hash, p);
3451 }
3452 } else { /* otherwise just skip those entries */
3453 int pos2 = pos;
3454
3455 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++, p++) {
3456 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3457 pos2++;
3458 zend_hash_del_bucket(in_hash, p);
3459 }
3460 }
3461 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3462
3463 /* If there are entries to insert.. */
3464 if (replace) {
3465 ZEND_HASH_FOREACH_VAL(replace, entry) {
3466 Z_TRY_ADDREF_P(entry);
3467 zend_hash_next_index_insert_new(&out_hash, entry);
3468 pos++;
3469 } ZEND_HASH_FOREACH_END();
3470 }
3471
3472 /* Copy the remaining input hash entries to the output hash */
3473 for ( ; idx < in_hash->nNumUsed ; idx++, p++) {
3474 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3475 entry = &p->val;
3476 if (p->key == NULL) {
3477 zend_hash_next_index_insert_new(&out_hash, entry);
3478 } else {
3479 zend_hash_add_new(&out_hash, p->key, entry);
3480 }
3481 if (idx == iter_pos) {
3482 if ((zend_long)idx != pos) {
3483 zend_hash_iterators_update(in_hash, idx, pos);
3484 }
3485 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3486 }
3487 pos++;
3488 }
3489 }
3490
3491 /* replace HashTable data */
3492 HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3493 HT_SET_ITERATORS_COUNT(in_hash, 0);
3494 in_hash->pDestructor = NULL;
3495 zend_hash_destroy(in_hash);
3496
3497 HT_FLAGS(in_hash) = HT_FLAGS(&out_hash);
3498 in_hash->nTableSize = out_hash.nTableSize;
3499 in_hash->nTableMask = out_hash.nTableMask;
3500 in_hash->nNumUsed = out_hash.nNumUsed;
3501 in_hash->nNumOfElements = out_hash.nNumOfElements;
3502 in_hash->nNextFreeElement = out_hash.nNextFreeElement;
3503 in_hash->arData = out_hash.arData;
3504 in_hash->pDestructor = out_hash.pDestructor;
3505
3506 zend_hash_internal_pointer_reset(in_hash);
3507 }
3508 /* }}} */
3509
3510 /* {{{ Pushes elements onto the end of the array */
3511 PHP_FUNCTION(array_push)
3512 {
3513 zval *args, /* Function arguments array */
3514 *stack, /* Input array */
3515 new_var; /* Variable to be pushed */
3516 uint32_t argc; /* Number of function arguments */
3517
3518
3519 ZEND_PARSE_PARAMETERS_START(1, -1)
3520 Z_PARAM_ARRAY_EX(stack, 0, 1)
3521 Z_PARAM_VARIADIC('+', args, argc)
3522 ZEND_PARSE_PARAMETERS_END();
3523
3524 /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3525 for (uint32_t i = 0; i < argc; i++) {
3526 ZVAL_COPY(&new_var, &args[i]);
3527
3528 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
3529 Z_TRY_DELREF(new_var);
3530 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3531 RETURN_THROWS();
3532 }
3533 }
3534
3535 /* Clean up and return the number of values in the stack */
3536 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3537 }
3538 /* }}} */
3539
3540 /* {{{ Pops an element off the end of the array */
3541 PHP_FUNCTION(array_pop)
3542 {
3543 zval *stack, /* Input stack */
3544 *val; /* Value to be popped */
3545 uint32_t idx;
3546
3547 ZEND_PARSE_PARAMETERS_START(1, 1)
3548 Z_PARAM_ARRAY_EX(stack, 0, 1)
3549 ZEND_PARSE_PARAMETERS_END();
3550
3551 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3552 return;
3553 }
3554
3555 if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3556 /* Get the last value and copy it into the return value */
3557 idx = Z_ARRVAL_P(stack)->nNumUsed;
3558 while (1) {
3559 if (idx == 0) {
3560 return;
3561 }
3562 idx--;
3563 val = Z_ARRVAL_P(stack)->arPacked + idx;
3564 if (Z_TYPE_P(val) != IS_UNDEF) {
3565 break;
3566 }
3567 }
3568 RETVAL_COPY_DEREF(val);
3569
3570 if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3571 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3572 }
3573
3574 /* Delete the last value */
3575 zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3576 } else {
3577 Bucket *p;
3578
3579 /* Get the last value and copy it into the return value */
3580 idx = Z_ARRVAL_P(stack)->nNumUsed;
3581 while (1) {
3582 if (idx == 0) {
3583 return;
3584 }
3585 idx--;
3586 p = Z_ARRVAL_P(stack)->arData + idx;
3587 val = &p->val;
3588 if (Z_TYPE_P(val) != IS_UNDEF) {
3589 break;
3590 }
3591 }
3592 RETVAL_COPY_DEREF(val);
3593
3594 if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3595 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3596 }
3597
3598 /* Delete the last value */
3599 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3600 }
3601 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3602 }
3603 /* }}} */
3604
3605 /* {{{ Pops an element off the beginning of the array */
3606 PHP_FUNCTION(array_shift)
3607 {
3608 zval *stack, /* Input stack */
3609 *val; /* Value to be popped */
3610 uint32_t idx;
3611
3612 ZEND_PARSE_PARAMETERS_START(1, 1)
3613 Z_PARAM_ARRAY_EX(stack, 0, 1)
3614 ZEND_PARSE_PARAMETERS_END();
3615
3616 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3617 return;
3618 }
3619
3620 /* re-index like it did before */
3621 if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3622 uint32_t k = 0;
3623
3624 /* Get the first value and copy it into the return value */
3625 idx = 0;
3626 while (1) {
3627 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3628 return;
3629 }
3630 val = Z_ARRVAL_P(stack)->arPacked + idx;
3631 if (Z_TYPE_P(val) != IS_UNDEF) {
3632 break;
3633 }
3634 idx++;
3635 }
3636 RETVAL_COPY_DEREF(val);
3637
3638 /* Delete the first value */
3639 zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3640
3641 if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3642 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3643 val = Z_ARRVAL_P(stack)->arPacked + idx;
3644 if (Z_TYPE_P(val) == IS_UNDEF) continue;
3645 if (idx != k) {
3646 zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3647 ZVAL_COPY_VALUE(q, val);
3648 ZVAL_UNDEF(val);
3649 }
3650 k++;
3651 }
3652 } else {
3653 uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3654
3655 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3656 val = Z_ARRVAL_P(stack)->arPacked + idx;
3657 if (Z_TYPE_P(val) == IS_UNDEF) continue;
3658 if (idx != k) {
3659 zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3660 ZVAL_COPY_VALUE(q, val);
3661 ZVAL_UNDEF(val);
3662 if (idx == iter_pos) {
3663 zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3664 iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3665 }
3666 }
3667 k++;
3668 }
3669 }
3670 Z_ARRVAL_P(stack)->nNumUsed = k;
3671 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3672 } else {
3673 uint32_t k = 0;
3674 int should_rehash = 0;
3675 Bucket *p;
3676
3677 /* Get the first value and copy it into the return value */
3678 idx = 0;
3679 while (1) {
3680 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3681 return;
3682 }
3683 p = Z_ARRVAL_P(stack)->arData + idx;
3684 val = &p->val;
3685 if (Z_TYPE_P(val) != IS_UNDEF) {
3686 break;
3687 }
3688 idx++;
3689 }
3690 RETVAL_COPY_DEREF(val);
3691
3692 /* Delete the first value */
3693 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3694
3695 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3696 p = Z_ARRVAL_P(stack)->arData + idx;
3697 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3698 if (p->key == NULL) {
3699 if (p->h != k) {
3700 p->h = k++;
3701 should_rehash = 1;
3702 } else {
3703 k++;
3704 }
3705 }
3706 }
3707 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3708 if (should_rehash) {
3709 zend_hash_rehash(Z_ARRVAL_P(stack));
3710 }
3711 }
3712
3713 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3714 }
3715 /* }}} */
3716
3717 /* {{{ Pushes elements onto the beginning of the array */
3718 PHP_FUNCTION(array_unshift)
3719 {
3720 zval *args, /* Function arguments array */
3721 *stack; /* Input stack */
3722 HashTable new_hash; /* New hashtable for the stack */
3723 uint32_t argc; /* Number of function arguments */
3724 zend_string *key;
3725 zval *value;
3726
3727 ZEND_PARSE_PARAMETERS_START(1, -1)
3728 Z_PARAM_ARRAY_EX(stack, 0, 1)
3729 Z_PARAM_VARIADIC('+', args, argc)
3730 ZEND_PARSE_PARAMETERS_END();
3731
3732 zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3733 for (uint32_t i = 0; i < argc; i++) {
3734 Z_TRY_ADDREF(args[i]);
3735 zend_hash_next_index_insert_new(&new_hash, &args[i]);
3736 }
3737
3738 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3739 if (key) {
3740 zend_hash_add_new(&new_hash, key, value);
3741 } else {
3742 zend_hash_next_index_insert_new(&new_hash, value);
3743 }
3744 } ZEND_HASH_FOREACH_END();
3745
3746 if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3747 zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3748 HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3749 HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3750 }
3751
3752 /* replace HashTable data */
3753 Z_ARRVAL_P(stack)->pDestructor = NULL;
3754 zend_hash_destroy(Z_ARRVAL_P(stack));
3755
3756 HT_FLAGS(Z_ARRVAL_P(stack)) = HT_FLAGS(&new_hash);
3757 Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
3758 Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
3759 Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
3760 Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
3761 Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
3762 Z_ARRVAL_P(stack)->arData = new_hash.arData;
3763 Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
3764
3765 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3766
3767 /* Clean up and return the number of elements in the stack */
3768 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3769 }
3770 /* }}} */
3771
3772 /* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3773 PHP_FUNCTION(array_splice)
3774 {
3775 zval *array, /* Input array */
3776 *repl_array = NULL; /* Replacement array */
3777 HashTable *rem_hash = NULL;
3778 zend_long offset,
3779 length = 0;
3780 bool length_is_null = 1;
3781 int num_in; /* Number of elements in the input array */
3782
3783 ZEND_PARSE_PARAMETERS_START(2, 4)
3784 Z_PARAM_ARRAY_EX(array, 0, 1)
3785 Z_PARAM_LONG(offset)
3786 Z_PARAM_OPTIONAL
3787 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3788 Z_PARAM_ZVAL(repl_array)
3789 ZEND_PARSE_PARAMETERS_END();
3790
3791 num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3792
3793 if (length_is_null) {
3794 length = num_in;
3795 }
3796
3797 if (ZEND_NUM_ARGS() == 4) {
3798 /* Make sure the last argument, if passed, is an array */
3799 convert_to_array(repl_array);
3800 }
3801
3802 /* Don't create the array of removed elements if it's not going
3803 * to be used; e.g. only removing and/or replacing elements */
3804 if (USED_RET()) {
3805 zend_long size = length;
3806
3807 /* Clamp the offset.. */
3808 if (offset > num_in) {
3809 offset = num_in;
3810 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3811 offset = 0;
3812 }
3813
3814 /* ..and the length */
3815 if (length < 0) {
3816 size = num_in - offset + length;
3817 } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
3818 size = num_in - offset;
3819 }
3820
3821 /* Initialize return value */
3822 array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3823 rem_hash = Z_ARRVAL_P(return_value);
3824 } else {
3825 /* The return value will not be used, but make sure it still has the correct type. */
3826 RETVAL_EMPTY_ARRAY();
3827 }
3828
3829 /* Perform splice */
3830 php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3831 }
3832 /* }}} */
3833
3834 /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3835 Finds the bucket at the given valid offset */
3836 static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3837 {
3838 zend_long pos;
3839 Bucket *bucket;
3840 ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3841 if (HT_IS_WITHOUT_HOLES(ht)) {
3842 /* There's no need to iterate over the array to filter out holes if there are no holes */
3843 /* This properly handles both packed and unpacked arrays. */
3844 return ht->arData + offset;
3845 }
3846 /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3847 pos = 0;
3848 ZEND_HASH_MAP_FOREACH_BUCKET(ht, bucket) {
3849 if (pos >= offset) {
3850 /* This is the bucket of the array element at the requested offset */
3851 return bucket;
3852 }
3853 ++pos;
3854 } ZEND_HASH_FOREACH_END();
3855
3856 /* Return a pointer to the end of the bucket array. */
3857 return ht->arData + ht->nNumUsed;
3858 }
3859 /* }}} */
3860
3861 /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3862 Finds the bucket at the given valid offset */
3863 static inline zval* find_packed_val_at_offset(HashTable* ht, zend_long offset)
3864 {
3865 zend_long pos;
3866 zval *zv;
3867 ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3868 if (HT_IS_WITHOUT_HOLES(ht)) {
3869 /* There's no need to iterate over the array to filter out holes if there are no holes */
3870 /* This properly handles both packed and unpacked arrays. */
3871 return ht->arPacked + offset;
3872 }
3873 /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3874 pos = 0;
3875 ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) {
3876 if (pos >= offset) {
3877 /* This is the bucket of the array element at the requested offset */
3878 return zv;
3879 }
3880 ++pos;
3881 } ZEND_HASH_FOREACH_END();
3882
3883 /* Return a pointer to the end of the bucket array. */
3884 return ht->arPacked + ht->nNumUsed;
3885 }
3886 /* }}} */
3887
3888 /* {{{ Returns elements specified by offset and length */
3889 PHP_FUNCTION(array_slice)
3890 {
3891 zval *input; /* Input array */
3892 zval *entry; /* An array entry */
3893 zend_long offset; /* Offset to get elements from */
3894 zend_long length = 0; /* How many elements to get */
3895 bool length_is_null = 1; /* Whether an explicit length has been omitted */
3896 bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array */
3897 uint32_t num_in; /* Number of elements in the input array */
3898 zend_string *string_key;
3899 zend_ulong num_key;
3900
3901 ZEND_PARSE_PARAMETERS_START(2, 4)
3902 Z_PARAM_ARRAY(input)
3903 Z_PARAM_LONG(offset)
3904 Z_PARAM_OPTIONAL
3905 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3906 Z_PARAM_BOOL(preserve_keys)
3907 ZEND_PARSE_PARAMETERS_END();
3908
3909 /* Get number of entries in the input hash */
3910 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
3911
3912 /* We want all entries from offset to the end if length is not passed or is null */
3913 if (length_is_null) {
3914 length = num_in;
3915 }
3916
3917 /* Clamp the offset.. */
3918 if (offset > (zend_long) num_in) {
3919 RETURN_EMPTY_ARRAY();
3920 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3921 offset = 0;
3922 }
3923
3924 /* ..and the length */
3925 if (length < 0) {
3926 length = num_in - offset + length;
3927 } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3928 length = num_in - offset;
3929 }
3930
3931 if (length <= 0) {
3932 RETURN_EMPTY_ARRAY();
3933 }
3934
3935 /* Initialize returned array */
3936 array_init_size(return_value, (uint32_t)length);
3937
3938 // Contains modified variants of ZEND_HASH_FOREACH_VAL
3939 {
3940 HashTable *ht = Z_ARRVAL_P(input);
3941
3942 /* Start at the beginning and go until we hit offset */
3943 if (HT_IS_PACKED(ht)) {
3944 zval *zv = find_packed_val_at_offset(ht, offset);
3945 zval *end = ht->arPacked + ht->nNumUsed;
3946
3947 if (!preserve_keys
3948 || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3949 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3950 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3951 for (; zv != end; zv++) {
3952 if (__fill_idx >= length) {
3953 break;
3954 }
3955 if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3956 continue;
3957 }
3958 entry = zv;
3959 if (UNEXPECTED(Z_ISREF_P(entry)) &&
3960 UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3961 entry = Z_REFVAL_P(entry);
3962 }
3963 Z_TRY_ADDREF_P(entry);
3964 ZEND_HASH_FILL_ADD(entry);
3965 }
3966 } ZEND_HASH_FILL_END();
3967 } else {
3968 zend_long n = 0; /* Current number of elements */
3969 zend_long idx = zv - ht->arPacked;
3970
3971 for (; zv != end; zv++, idx++) {
3972 if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3973 continue;
3974 }
3975 if (n >= length) {
3976 break;
3977 }
3978 n++;
3979 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, zv);
3980 zval_add_ref(entry);
3981 }
3982 }
3983 } else {
3984 zend_long n = 0; /* Current number of elements */
3985 Bucket *p = find_bucket_at_offset(ht, offset);
3986 Bucket *end = ht->arData + ht->nNumUsed;
3987
3988 for (; p != end; p++) {
3989 entry = &p->val;
3990 if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3991 continue;
3992 }
3993 if (n >= length) {
3994 break;
3995 }
3996 n++;
3997 num_key = p->h;
3998 string_key = p->key;
3999
4000 if (string_key) {
4001 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4002 } else {
4003 if (preserve_keys) {
4004 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4005 } else {
4006 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4007 }
4008 }
4009 zval_add_ref(entry);
4010 }
4011 }
4012 }
4013 }
4014 /* }}} */
4015
4016 PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
4017 {
4018 zval *src_entry, *dest_entry;
4019 zend_string *string_key;
4020
4021 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4022 if (string_key) {
4023 if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
4024 zval *src_zval = src_entry;
4025 zval *dest_zval = dest_entry;
4026 HashTable *thash;
4027 zval tmp;
4028 int ret;
4029
4030 ZVAL_DEREF(src_zval);
4031 ZVAL_DEREF(dest_zval);
4032 thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
4033 if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
4034 zend_throw_error(NULL, "Recursion detected");
4035 return 0;
4036 }
4037
4038 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4039 SEPARATE_ZVAL(dest_entry);
4040 dest_zval = dest_entry;
4041
4042 if (Z_TYPE_P(dest_zval) == IS_NULL) {
4043 convert_to_array(dest_zval);
4044 add_next_index_null(dest_zval);
4045 } else {
4046 convert_to_array(dest_zval);
4047 }
4048 ZVAL_UNDEF(&tmp);
4049 if (Z_TYPE_P(src_zval) == IS_OBJECT) {
4050 ZVAL_COPY(&tmp, src_zval);
4051 convert_to_array(&tmp);
4052 src_zval = &tmp;
4053 }
4054 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
4055 if (thash) {
4056 GC_TRY_PROTECT_RECURSION(thash);
4057 }
4058 ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4059 if (thash) {
4060 GC_TRY_UNPROTECT_RECURSION(thash);
4061 }
4062 if (!ret) {
4063 return 0;
4064 }
4065 } else {
4066 Z_TRY_ADDREF_P(src_zval);
4067 zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
4068 if (EXPECTED(!zv)) {
4069 Z_TRY_DELREF_P(src_zval);
4070 zend_cannot_add_element();
4071 return 0;
4072 }
4073 }
4074 zval_ptr_dtor(&tmp);
4075 } else {
4076 zval *zv = zend_hash_add_new(dest, string_key, src_entry);
4077 zval_add_ref(zv);
4078 }
4079 } else {
4080 zval *zv = zend_hash_next_index_insert(dest, src_entry);
4081 if (UNEXPECTED(!zv)) {
4082 zend_cannot_add_element();
4083 return 0;
4084 }
4085 zval_add_ref(zv);
4086 }
4087 } ZEND_HASH_FOREACH_END();
4088 return 1;
4089 }
4090 /* }}} */
4091
4092 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
4093 {
4094 zval *src_entry;
4095 zend_string *string_key;
4096
4097 if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
4098 zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
4099 ZEND_HASH_FILL_PACKED(dest) {
4100 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4101 if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
4102 UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
4103 src_entry = Z_REFVAL_P(src_entry);
4104 }
4105 Z_TRY_ADDREF_P(src_entry);
4106 ZEND_HASH_FILL_ADD(src_entry);
4107 } ZEND_HASH_FOREACH_END();
4108 } ZEND_HASH_FILL_END();
4109 } else {
4110 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4111 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4112 Z_REFCOUNT_P(src_entry) == 1)) {
4113 src_entry = Z_REFVAL_P(src_entry);
4114 }
4115 Z_TRY_ADDREF_P(src_entry);
4116 if (string_key) {
4117 zend_hash_update(dest, string_key, src_entry);
4118 } else {
4119 zend_hash_next_index_insert_new(dest, src_entry);
4120 }
4121 } ZEND_HASH_FOREACH_END();
4122 }
4123 return 1;
4124 }
4125 /* }}} */
4126
4127 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4128 {
4129 zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4130 zend_string *string_key;
4131 zend_ulong num_key;
4132 int ret;
4133
4134 ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4135 src_zval = src_entry;
4136 ZVAL_DEREF(src_zval);
4137 if (string_key) {
4138 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4139 (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4140 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4141 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4142
4143 zval *zv = zend_hash_update(dest, string_key, src_entry);
4144 zval_add_ref(zv);
4145 continue;
4146 }
4147 } else {
4148 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4149 (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4150 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4151 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4152
4153 zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4154 zval_add_ref(zv);
4155 continue;
4156 }
4157 }
4158
4159 dest_zval = dest_entry;
4160 ZVAL_DEREF(dest_zval);
4161 if (Z_IS_RECURSIVE_P(dest_zval) ||
4162 Z_IS_RECURSIVE_P(src_zval) ||
4163 (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))) {
4164 zend_throw_error(NULL, "Recursion detected");
4165 return 0;
4166 }
4167
4168 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4169 SEPARATE_ZVAL(dest_entry);
4170 dest_zval = dest_entry;
4171
4172 if (Z_REFCOUNTED_P(dest_zval)) {
4173 Z_PROTECT_RECURSION_P(dest_zval);
4174 }
4175 if (Z_REFCOUNTED_P(src_zval)) {
4176 Z_PROTECT_RECURSION_P(src_zval);
4177 }
4178
4179 ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4180
4181 if (Z_REFCOUNTED_P(dest_zval)) {
4182 Z_UNPROTECT_RECURSION_P(dest_zval);
4183 }
4184 if (Z_REFCOUNTED_P(src_zval)) {
4185 Z_UNPROTECT_RECURSION_P(src_zval);
4186 }
4187
4188 if (!ret) {
4189 return 0;
4190 }
4191 } ZEND_HASH_FOREACH_END();
4192
4193 return 1;
4194 }
4195 /* }}} */
4196
4197 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4198 {
4199 zval *args = NULL;
4200 zval *arg;
4201 uint32_t argc, i;
4202 HashTable *dest;
4203
4204 ZEND_PARSE_PARAMETERS_START(1, -1)
4205 Z_PARAM_VARIADIC('+', args, argc)
4206 ZEND_PARSE_PARAMETERS_END();
4207
4208
4209 for (i = 0; i < argc; i++) {
4210 zval *arg = args + i;
4211
4212 if (Z_TYPE_P(arg) != IS_ARRAY) {
4213 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4214 RETURN_THROWS();
4215 }
4216 }
4217
4218 /* copy first array if necessary */
4219 arg = args;
4220 bool in_place = zend_may_modify_arg_in_place(arg);
4221 if (in_place) {
4222 dest = Z_ARRVAL_P(arg);
4223 } else {
4224 dest = zend_array_dup(Z_ARRVAL_P(arg));
4225 }
4226
4227 ZVAL_ARR(return_value, dest);
4228
4229 if (recursive) {
4230 for (i = 1; i < argc; i++) {
4231 arg = args + i;
4232 php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4233 }
4234 } else {
4235 for (i = 1; i < argc; i++) {
4236 arg = args + i;
4237 zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4238 }
4239 }
4240
4241 if (in_place) {
4242 GC_ADDREF(dest);
4243 }
4244 }
4245 /* }}} */
4246
4247 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4248 {
4249 zval *args = NULL;
4250 zval *arg;
4251 uint32_t argc, i;
4252 zval *src_entry;
4253 HashTable *src, *dest;
4254 uint32_t count = 0;
4255
4256 ZEND_PARSE_PARAMETERS_START(0, -1)
4257 Z_PARAM_VARIADIC('+', args, argc)
4258 ZEND_PARSE_PARAMETERS_END();
4259
4260 if (argc == 0) {
4261 RETURN_EMPTY_ARRAY();
4262 }
4263
4264 for (i = 0; i < argc; i++) {
4265 zval *arg = args + i;
4266
4267 if (Z_TYPE_P(arg) != IS_ARRAY) {
4268 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4269 RETURN_THROWS();
4270 }
4271 count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4272 }
4273
4274 if (argc == 2) {
4275 zval *ret = NULL;
4276
4277 if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4278 ret = &args[1];
4279 } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4280 ret = &args[0];
4281 }
4282 if (ret) {
4283 if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4284 if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4285 ZVAL_COPY(return_value, ret);
4286 return;
4287 }
4288 } else {
4289 bool copy = 1;
4290 zend_string *string_key;
4291
4292 ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4293 if (!string_key) {
4294 copy = 0;
4295 break;
4296 }
4297 } ZEND_HASH_FOREACH_END();
4298 if (copy) {
4299 ZVAL_COPY(return_value, ret);
4300 return;
4301 }
4302 }
4303 }
4304 }
4305
4306 arg = args;
4307 src = Z_ARRVAL_P(arg);
4308 /* copy first array if necessary */
4309 bool in_place = false;
4310 if (HT_IS_PACKED(src)) {
4311 /* Note: If it has holes, it might get sequentialized */
4312 if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4313 dest = src;
4314 in_place = true;
4315 ZVAL_ARR(return_value, dest);
4316 } else {
4317 array_init_size(return_value, count);
4318 dest = Z_ARRVAL_P(return_value);
4319
4320 zend_hash_real_init_packed(dest);
4321 ZEND_HASH_FILL_PACKED(dest) {
4322 ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4323 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4324 Z_REFCOUNT_P(src_entry) == 1)) {
4325 src_entry = Z_REFVAL_P(src_entry);
4326 }
4327 Z_TRY_ADDREF_P(src_entry);
4328 ZEND_HASH_FILL_ADD(src_entry);
4329 } ZEND_HASH_FOREACH_END();
4330 } ZEND_HASH_FILL_END();
4331 }
4332 } else {
4333 array_init_size(return_value, count);
4334 dest = Z_ARRVAL_P(return_value);
4335
4336 zend_string *string_key;
4337 zend_hash_real_init_mixed(dest);
4338 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4339 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4340 Z_REFCOUNT_P(src_entry) == 1)) {
4341 src_entry = Z_REFVAL_P(src_entry);
4342 }
4343 Z_TRY_ADDREF_P(src_entry);
4344 if (EXPECTED(string_key)) {
4345 _zend_hash_append(dest, string_key, src_entry);
4346 } else {
4347 zend_hash_next_index_insert_new(dest, src_entry);
4348 }
4349 } ZEND_HASH_FOREACH_END();
4350 }
4351 if (recursive) {
4352 for (i = 1; i < argc; i++) {
4353 arg = args + i;
4354 php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4355 }
4356 } else {
4357 for (i = 1; i < argc; i++) {
4358 arg = args + i;
4359 php_array_merge(dest, Z_ARRVAL_P(arg));
4360 }
4361 }
4362
4363 if (in_place) {
4364 GC_ADDREF(dest);
4365 }
4366 }
4367 /* }}} */
4368
4369 /* {{{ Merges elements from passed arrays into one array */
4370 PHP_FUNCTION(array_merge)
4371 {
4372 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4373 }
4374 /* }}} */
4375
4376 /* {{{ Recursively merges elements from passed arrays into one array */
4377 PHP_FUNCTION(array_merge_recursive)
4378 {
4379 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4380 }
4381 /* }}} */
4382
4383 /* {{{ Replaces elements from passed arrays into one array */
4384 PHP_FUNCTION(array_replace)
4385 {
4386 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4387 }
4388 /* }}} */
4389
4390 /* {{{ Recursively replaces elements from passed arrays into one array */
4391 PHP_FUNCTION(array_replace_recursive)
4392 {
4393 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4394 }
4395 /* }}} */
4396
4397 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4398 PHP_FUNCTION(array_keys)
4399 {
4400 zval *input, /* Input array */
4401 *search_value = NULL, /* Value to search for */
4402 *entry, /* An entry in the input array */
4403 new_val; /* New value */
4404 bool strict = 0; /* do strict comparison */
4405 zend_ulong num_idx;
4406 zend_string *str_idx;
4407 zend_array *arrval;
4408 zend_ulong elem_count;
4409
4410 ZEND_PARSE_PARAMETERS_START(1, 3)
4411 Z_PARAM_ARRAY(input)
4412 Z_PARAM_OPTIONAL
4413 Z_PARAM_ZVAL(search_value)
4414 Z_PARAM_BOOL(strict)
4415 ZEND_PARSE_PARAMETERS_END();
4416 arrval = Z_ARRVAL_P(input);
4417 elem_count = zend_hash_num_elements(arrval);
4418
4419 /* Base case: empty input */
4420 if (!elem_count) {
4421 RETURN_COPY(input);
4422 }
4423
4424 /* Initialize return array */
4425 if (search_value != NULL) {
4426 array_init(return_value);
4427
4428 if (strict) {
4429 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4430 ZVAL_DEREF(entry);
4431 if (fast_is_identical_function(search_value, entry)) {
4432 if (str_idx) {
4433 ZVAL_STR_COPY(&new_val, str_idx);
4434 } else {
4435 ZVAL_LONG(&new_val, num_idx);
4436 }
4437 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4438 }
4439 } ZEND_HASH_FOREACH_END();
4440 } else {
4441 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4442 if (fast_equal_check_function(search_value, entry)) {
4443 if (str_idx) {
4444 ZVAL_STR_COPY(&new_val, str_idx);
4445 } else {
4446 ZVAL_LONG(&new_val, num_idx);
4447 }
4448 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4449 }
4450 } ZEND_HASH_FOREACH_END();
4451 }
4452 } else {
4453 array_init_size(return_value, elem_count);
4454 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4455 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4456 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4457 /* Optimistic case: range(0..n-1) for vector-like packed array */
4458 zend_ulong lval = 0;
4459
4460 for (; lval < elem_count; ++lval) {
4461 ZEND_HASH_FILL_SET_LONG(lval);
4462 ZEND_HASH_FILL_NEXT();
4463 }
4464 } else {
4465 /* Go through input array and add keys to the return array */
4466 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4467 if (str_idx) {
4468 ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4469 } else {
4470 ZEND_HASH_FILL_SET_LONG(num_idx);
4471 }
4472 ZEND_HASH_FILL_NEXT();
4473 } ZEND_HASH_FOREACH_END();
4474 }
4475 } ZEND_HASH_FILL_END();
4476 }
4477 }
4478 /* }}} */
4479
4480 /* {{{ Get the key of the first element of the array */
4481 PHP_FUNCTION(array_key_first)
4482 {
4483 zval *stack; /* Input stack */
4484
4485 ZEND_PARSE_PARAMETERS_START(1, 1)
4486 Z_PARAM_ARRAY(stack)
4487 ZEND_PARSE_PARAMETERS_END();
4488
4489 HashTable *target_hash = Z_ARRVAL_P (stack);
4490 HashPosition pos = 0;
4491 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4492 }
4493 /* }}} */
4494
4495 /* {{{ Get the key of the last element of the array */
4496 PHP_FUNCTION(array_key_last)
4497 {
4498 zval *stack; /* Input stack */
4499 HashPosition pos;
4500
4501 ZEND_PARSE_PARAMETERS_START(1, 1)
4502 Z_PARAM_ARRAY(stack)
4503 ZEND_PARSE_PARAMETERS_END();
4504
4505 HashTable *target_hash = Z_ARRVAL_P (stack);
4506 zend_hash_internal_pointer_end_ex(target_hash, &pos);
4507 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4508 }
4509 /* }}} */
4510
4511 /* {{{ Return just the values from the input array */
4512 PHP_FUNCTION(array_values)
4513 {
4514 zval *input; /* Input array */
4515 zend_array *arrval;
4516 zend_long arrlen;
4517
4518 ZEND_PARSE_PARAMETERS_START(1, 1)
4519 Z_PARAM_ARRAY(input)
4520 ZEND_PARSE_PARAMETERS_END();
4521
4522 arrval = Z_ARRVAL_P(input);
4523
4524 /* Return empty input as is */
4525 arrlen = zend_hash_num_elements(arrval);
4526 if (!arrlen) {
4527 RETURN_EMPTY_ARRAY();
4528 }
4529
4530 /* Return vector-like packed arrays as-is */
4531 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4532 arrval->nNextFreeElement == arrlen) {
4533 RETURN_COPY(input);
4534 }
4535
4536 RETURN_ARR(zend_array_to_list(arrval));
4537 }
4538 /* }}} */
4539
4540 /* {{{ Return the value as key and the frequency of that value in input as value */
4541 PHP_FUNCTION(array_count_values)
4542 {
4543 zval *input, /* Input array */
4544 *entry, /* An entry in the input array */
4545 *tmp;
4546 HashTable *myht;
4547
4548 ZEND_PARSE_PARAMETERS_START(1, 1)
4549 Z_PARAM_ARRAY(input)
4550 ZEND_PARSE_PARAMETERS_END();
4551
4552 /* Initialize return array */
4553 array_init(return_value);
4554
4555 /* Go through input array and add values to the return array */
4556 myht = Z_ARRVAL_P(input);
4557 ZEND_HASH_FOREACH_VAL(myht, entry) {
4558 ZVAL_DEREF(entry);
4559 if (Z_TYPE_P(entry) == IS_LONG) {
4560 if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4561 zval data;
4562 ZVAL_LONG(&data, 1);
4563 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4564 } else {
4565 Z_LVAL_P(tmp)++;
4566 }
4567 } else if (Z_TYPE_P(entry) == IS_STRING) {
4568 if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4569 zval data;
4570 ZVAL_LONG(&data, 1);
4571 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4572 } else {
4573 Z_LVAL_P(tmp)++;
4574 }
4575 } else {
4576 php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4577 }
4578 } ZEND_HASH_FOREACH_END();
4579 }
4580 /* }}} */
4581
4582 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4583 {
4584 zval *prop = NULL;
4585
4586 if (Z_TYPE_P(data) == IS_OBJECT) {
4587 zend_string *tmp_str;
4588 /* If name is an integer convert integer to string */
4589 if (name_str == NULL) {
4590 tmp_str = zend_long_to_str(name_long);
4591 } else {
4592 tmp_str = zend_string_copy(name_str);
4593 }
4594 /* The has_property check is first performed in "exists" mode (which returns true for
4595 * properties that are null but exist) and then in "has" mode to handle objects that
4596 * implement __isset (which is not called in "exists" mode). */
4597 if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4598 || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4599 prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4600 if (prop) {
4601 ZVAL_DEREF(prop);
4602 if (prop != rv) {
4603 Z_TRY_ADDREF_P(prop);
4604 }
4605 }
4606 }
4607 zend_string_release(tmp_str);
4608 } else if (Z_TYPE_P(data) == IS_ARRAY) {
4609 /* Name is a string */
4610 if (name_str != NULL) {
4611 prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4612 } else {
4613 prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4614 }
4615 if (prop) {
4616 ZVAL_DEREF(prop);
4617 Z_TRY_ADDREF_P(prop);
4618 }
4619 }
4620
4621 return prop;
4622 }
4623 /* }}} */
4624
4625 /* {{{ Return the values from a single column in the input array, identified by the
4626 value_key and optionally indexed by the index_key */
4627 PHP_FUNCTION(array_column)
4628 {
4629 HashTable *input;
4630 zval *colval, *data, rv;
4631 zend_string *column_str = NULL;
4632 zend_long column_long = 0;
4633 bool column_is_null = 0;
4634 zend_string *index_str = NULL;
4635 zend_long index_long = 0;
4636 bool index_is_null = 1;
4637
4638 ZEND_PARSE_PARAMETERS_START(2, 3)
4639 Z_PARAM_ARRAY_HT(input)
4640 Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4641 Z_PARAM_OPTIONAL
4642 Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4643 ZEND_PARSE_PARAMETERS_END();
4644
4645 void* cache_slot_column[3] = { NULL, NULL, NULL };
4646 void* cache_slot_index[3] = { NULL, NULL, NULL };
4647
4648 array_init_size(return_value, zend_hash_num_elements(input));
4649 /* Index param is not passed */
4650 if (index_is_null) {
4651 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4652 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4653 ZEND_HASH_FOREACH_VAL(input, data) {
4654 ZVAL_DEREF(data);
4655 if (column_is_null) {
4656 Z_TRY_ADDREF_P(data);
4657 colval = data;
4658 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4659 continue;
4660 }
4661 ZEND_HASH_FILL_ADD(colval);
4662 } ZEND_HASH_FOREACH_END();
4663 } ZEND_HASH_FILL_END();
4664 } else {
4665 ZEND_HASH_FOREACH_VAL(input, data) {
4666 ZVAL_DEREF(data);
4667
4668 if (column_is_null) {
4669 Z_TRY_ADDREF_P(data);
4670 colval = data;
4671 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4672 continue;
4673 }
4674
4675 zval rv;
4676 zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4677 if (keyval) {
4678 array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4679 zval_ptr_dtor(colval);
4680 zval_ptr_dtor(keyval);
4681 } else {
4682 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4683 }
4684 } ZEND_HASH_FOREACH_END();
4685 }
4686 }
4687 /* }}} */
4688
4689 /* {{{ Return input as a new array with the order of the entries reversed */
4690 PHP_FUNCTION(array_reverse)
4691 {
4692 zval *input, /* Input array */
4693 *entry; /* An entry in the input array */
4694 zend_string *string_key;
4695 zend_ulong num_key;
4696 bool preserve_keys = 0; /* whether to preserve keys */
4697
4698 ZEND_PARSE_PARAMETERS_START(1, 2)
4699 Z_PARAM_ARRAY(input)
4700 Z_PARAM_OPTIONAL
4701 Z_PARAM_BOOL(preserve_keys)
4702 ZEND_PARSE_PARAMETERS_END();
4703
4704 /* Initialize return array */
4705 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4706 if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4707 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4708 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4709 ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4710 if (UNEXPECTED(Z_ISREF_P(entry) &&
4711 Z_REFCOUNT_P(entry) == 1)) {
4712 entry = Z_REFVAL_P(entry);
4713 }
4714 Z_TRY_ADDREF_P(entry);
4715 ZEND_HASH_FILL_ADD(entry);
4716 } ZEND_HASH_FOREACH_END();
4717 } ZEND_HASH_FILL_END();
4718 } else {
4719 ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4720 if (string_key) {
4721 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4722 } else {
4723 if (preserve_keys) {
4724 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4725 } else {
4726 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4727 }
4728 }
4729 zval_add_ref(entry);
4730 } ZEND_HASH_FOREACH_END();
4731 }
4732 }
4733 /* }}} */
4734
4735 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4736 PHP_FUNCTION(array_pad)
4737 {
4738 zval *input; /* Input array */
4739 zval *pad_value; /* Padding value obviously */
4740 zend_long pad_size; /* Size to pad to */
4741 zend_long pad_size_abs; /* Absolute value of pad_size */
4742 zend_long input_size; /* Size of the input array */
4743 zend_long num_pads; /* How many pads do we need */
4744 zend_long i;
4745 zend_string *key;
4746 zval *value;
4747
4748 ZEND_PARSE_PARAMETERS_START(3, 3)
4749 Z_PARAM_ARRAY(input)
4750 Z_PARAM_LONG(pad_size)
4751 Z_PARAM_ZVAL(pad_value)
4752 ZEND_PARSE_PARAMETERS_END();
4753
4754 if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4755 zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4756 RETURN_THROWS();
4757 }
4758
4759 /* Do some initial calculations */
4760 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4761 pad_size_abs = ZEND_ABS(pad_size);
4762
4763 if (input_size >= pad_size_abs) {
4764 /* Copy the original array */
4765 ZVAL_COPY(return_value, input);
4766 return;
4767 }
4768
4769 num_pads = pad_size_abs - input_size;
4770 if (Z_REFCOUNTED_P(pad_value)) {
4771 GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4772 }
4773
4774 array_init_size(return_value, pad_size_abs);
4775 if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4776 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4777
4778 if (pad_size < 0) {
4779 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4780 for (i = 0; i < num_pads; i++) {
4781 ZEND_HASH_FILL_ADD(pad_value);
4782 }
4783 } ZEND_HASH_FILL_END();
4784 }
4785
4786 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4787 ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4788 Z_TRY_ADDREF_P(value);
4789 ZEND_HASH_FILL_ADD(value);
4790 } ZEND_HASH_FOREACH_END();
4791 } ZEND_HASH_FILL_END();
4792
4793 if (pad_size > 0) {
4794 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4795 for (i = 0; i < num_pads; i++) {
4796 ZEND_HASH_FILL_ADD(pad_value);
4797 }
4798 } ZEND_HASH_FILL_END();
4799 }
4800 } else {
4801 if (pad_size < 0) {
4802 for (i = 0; i < num_pads; i++) {
4803 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4804 }
4805 }
4806
4807 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4808 Z_TRY_ADDREF_P(value);
4809 if (key) {
4810 zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4811 } else {
4812 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4813 }
4814 } ZEND_HASH_FOREACH_END();
4815
4816 if (pad_size > 0) {
4817 for (i = 0; i < num_pads; i++) {
4818 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4819 }
4820 }
4821 }
4822 }
4823 /* }}} */
4824
4825 /* {{{ Return array with key <-> value flipped */
4826 PHP_FUNCTION(array_flip)
4827 {
4828 zval *array, *entry, data;
4829 zend_ulong num_idx;
4830 zend_string *str_idx;
4831
4832 ZEND_PARSE_PARAMETERS_START(1, 1)
4833 Z_PARAM_ARRAY(array)
4834 ZEND_PARSE_PARAMETERS_END();
4835
4836 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4837
4838 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4839 ZVAL_DEREF(entry);
4840 if (Z_TYPE_P(entry) == IS_LONG) {
4841 if (str_idx) {
4842 ZVAL_STR_COPY(&data, str_idx);
4843 } else {
4844 ZVAL_LONG(&data, num_idx);
4845 }
4846 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4847 } else if (Z_TYPE_P(entry) == IS_STRING) {
4848 if (str_idx) {
4849 ZVAL_STR_COPY(&data, str_idx);
4850 } else {
4851 ZVAL_LONG(&data, num_idx);
4852 }
4853 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4854 } else {
4855 php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4856 }
4857 } ZEND_HASH_FOREACH_END();
4858 }
4859 /* }}} */
4860
4861 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4862 PHP_FUNCTION(array_change_key_case)
4863 {
4864 zval *array, *entry;
4865 zend_string *string_key;
4866 zend_string *new_key;
4867 zend_ulong num_key;
4868 zend_long change_to_upper=0;
4869
4870 ZEND_PARSE_PARAMETERS_START(1, 2)
4871 Z_PARAM_ARRAY(array)
4872 Z_PARAM_OPTIONAL
4873 Z_PARAM_LONG(change_to_upper)
4874 ZEND_PARSE_PARAMETERS_END();
4875
4876 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4877
4878 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4879 if (!string_key) {
4880 entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4881 } else {
4882 if (change_to_upper) {
4883 new_key = zend_string_toupper(string_key);
4884 } else {
4885 new_key = zend_string_tolower(string_key);
4886 }
4887 entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4888 zend_string_release_ex(new_key, 0);
4889 }
4890
4891 zval_add_ref(entry);
4892 } ZEND_HASH_FOREACH_END();
4893 }
4894 /* }}} */
4895
4896 struct bucketindex {
4897 Bucket b;
4898 unsigned int i;
4899 };
4900
4901 static void array_bucketindex_swap(void *p, void *q)
4902 {
4903 struct bucketindex *f = (struct bucketindex *)p;
4904 struct bucketindex *g = (struct bucketindex *)q;
4905 struct bucketindex t;
4906 t = *f;
4907 *f = *g;
4908 *g = t;
4909 }
4910
4911 /* {{{ Removes duplicate values from array */
4912 PHP_FUNCTION(array_unique)
4913 {
4914 zval *array;
4915 Bucket *p;
4916 zend_long sort_type = PHP_SORT_STRING;
4917 bucket_compare_func_t cmp;
4918 struct bucketindex *arTmp, *cmpdata, *lastkept;
4919 uint32_t i, idx;
4920
4921 ZEND_PARSE_PARAMETERS_START(1, 2)
4922 Z_PARAM_ARRAY(array)
4923 Z_PARAM_OPTIONAL
4924 Z_PARAM_LONG(sort_type)
4925 ZEND_PARSE_PARAMETERS_END();
4926
4927 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4928 ZVAL_COPY(return_value, array);
4929 return;
4930 }
4931
4932 if (sort_type == PHP_SORT_STRING) {
4933 HashTable seen;
4934 zend_long num_key;
4935 zend_string *str_key;
4936 zval *val;
4937
4938 zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4939 array_init(return_value);
4940
4941 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4942 zval *retval;
4943 if (Z_TYPE_P(val) == IS_STRING) {
4944 retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4945 } else {
4946 zend_string *tmp_str_val;
4947 zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4948 retval = zend_hash_add_empty_element(&seen, str_val);
4949 zend_tmp_string_release(tmp_str_val);
4950 }
4951
4952 if (retval) {
4953 /* First occurrence of the value */
4954 if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4955 ZVAL_DEREF(val);
4956 }
4957 Z_TRY_ADDREF_P(val);
4958
4959 if (str_key) {
4960 zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4961 } else {
4962 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4963 }
4964 }
4965 } ZEND_HASH_FOREACH_END();
4966
4967 zend_hash_destroy(&seen);
4968 return;
4969 }
4970
4971 cmp = php_get_data_compare_func_unstable(sort_type, 0);
4972
4973 bool in_place = zend_may_modify_arg_in_place(array);
4974 if (in_place) {
4975 RETVAL_ARR(Z_ARRVAL_P(array));
4976 } else {
4977 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4978 }
4979
4980 /* create and sort array with pointers to the target_hash buckets */
4981 arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4982 if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4983 zval *zv = Z_ARRVAL_P(array)->arPacked;
4984 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4985 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4986 ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4987 arTmp[i].b.h = idx;
4988 arTmp[i].b.key = NULL;
4989 arTmp[i].i = i;
4990 i++;
4991 }
4992 } else {
4993 p = Z_ARRVAL_P(array)->arData;
4994 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4995 if (Z_TYPE(p->val) == IS_UNDEF) continue;
4996 arTmp[i].b = *p;
4997 arTmp[i].i = i;
4998 i++;
4999 }
5000 }
5001 ZVAL_UNDEF(&arTmp[i].b.val);
5002 zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
5003 (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
5004 /* go through the sorted array and delete duplicates from the copy */
5005 lastkept = arTmp;
5006 for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
5007 if (cmp(&lastkept->b, &cmpdata->b)) {
5008 lastkept = cmpdata;
5009 } else {
5010 if (lastkept->i > cmpdata->i) {
5011 p = &lastkept->b;
5012 lastkept = cmpdata;
5013 } else {
5014 p = &cmpdata->b;
5015 }
5016 if (p->key == NULL) {
5017 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5018 } else {
5019 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5020 }
5021 }
5022 }
5023 pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5024
5025 if (in_place) {
5026 Z_ADDREF_P(return_value);
5027 }
5028 }
5029 /* }}} */
5030
5031 static int zval_compare(zval *first, zval *second) /* {{{ */
5032 {
5033 return string_compare_function(first, second);
5034 }
5035 /* }}} */
5036
5037 static int zval_user_compare(zval *a, zval *b) /* {{{ */
5038 {
5039 zval args[2];
5040 zval retval;
5041
5042 ZVAL_COPY_VALUE(&args[0], a);
5043 ZVAL_COPY_VALUE(&args[1], b);
5044
5045 BG(user_compare_fci).param_count = 2;
5046 BG(user_compare_fci).params = args;
5047 BG(user_compare_fci).retval = &retval;
5048
5049 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
5050 zend_long ret = zval_get_long(&retval);
5051 zval_ptr_dtor(&retval);
5052 return ZEND_NORMALIZE_BOOL(ret);
5053 } else {
5054 return 0;
5055 }
5056 }
5057 /* }}} */
5058
5059 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5060 {
5061 uint32_t argc, i;
5062 zval *args;
5063 int (*intersect_data_compare_func)(zval *, zval *) = NULL;
5064 bool ok;
5065 zval *val, *data;
5066 char *param_spec;
5067 zend_string *key;
5068 zend_ulong h;
5069
5070 /* Get the argument count */
5071 argc = ZEND_NUM_ARGS();
5072 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5073 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
5074 param_spec = "+f";
5075 intersect_data_compare_func = zval_user_compare;
5076 } else {
5077 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
5078 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
5079 param_spec = "+";
5080
5081 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5082 intersect_data_compare_func = zval_compare;
5083 }
5084 }
5085
5086 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5087 RETURN_THROWS();
5088 }
5089
5090 for (i = 0; i < argc; i++) {
5091 if (Z_TYPE(args[i]) != IS_ARRAY) {
5092 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5093 RETURN_THROWS();
5094 }
5095 }
5096
5097 array_init(return_value);
5098
5099 /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
5100 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5101 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5102 val = Z_REFVAL_P(val);
5103 }
5104 if (key == NULL) {
5105 ok = 1;
5106 for (i = 1; i < argc; i++) {
5107 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
5108 (intersect_data_compare_func &&
5109 intersect_data_compare_func(val, data) != 0)
5110 ) {
5111 ok = 0;
5112 break;
5113 }
5114 }
5115 if (ok) {
5116 Z_TRY_ADDREF_P(val);
5117 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5118 }
5119 } else {
5120 ok = 1;
5121 for (i = 1; i < argc; i++) {
5122 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5123 (intersect_data_compare_func &&
5124 intersect_data_compare_func(val, data) != 0)
5125 ) {
5126 ok = 0;
5127 break;
5128 }
5129 }
5130 if (ok) {
5131 Z_TRY_ADDREF_P(val);
5132 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5133 }
5134 }
5135 } ZEND_HASH_FOREACH_END();
5136 }
5137 /* }}} */
5138
5139 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5140 {
5141 zval *args = NULL;
5142 HashTable *hash;
5143 uint32_t arr_argc, i;
5144 int c = 0;
5145 uint32_t idx;
5146 Bucket **lists, *list, **ptrs, *p;
5147 char *param_spec;
5148 zend_fcall_info fci1, fci2;
5149 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5150 zend_fcall_info *fci_key = NULL, *fci_data;
5151 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5152 PHP_ARRAY_CMP_FUNC_VARS;
5153 bool in_place = false;
5154
5155 bucket_compare_func_t intersect_key_compare_func;
5156 bucket_compare_func_t intersect_data_compare_func;
5157
5158 if (behavior == INTERSECT_NORMAL) {
5159 intersect_key_compare_func = php_array_key_compare_string;
5160
5161 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5162 /* array_intersect() */
5163 param_spec = "+";
5164 intersect_data_compare_func = php_array_data_compare_string_unstable;
5165 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5166 /* array_uintersect() */
5167 param_spec = "+f";
5168 intersect_data_compare_func = php_array_user_compare_unstable;
5169 } else {
5170 ZEND_ASSERT(0 && "Invalid data_compare_type");
5171 return;
5172 }
5173
5174 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5175 RETURN_THROWS();
5176 }
5177 fci_data = &fci1;
5178 fci_data_cache = &fci1_cache;
5179
5180 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5181 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5182 * no comparison of the data is done (part of INTERSECT_ASSOC) */
5183
5184 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5185 /* array_intersect_assoc() or array_intersect_key() */
5186 param_spec = "+";
5187 intersect_key_compare_func = php_array_key_compare_string_unstable;
5188 intersect_data_compare_func = php_array_data_compare_string_unstable;
5189 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5190 /* array_uintersect_assoc() */
5191 param_spec = "+f";
5192 intersect_key_compare_func = php_array_key_compare_string_unstable;
5193 intersect_data_compare_func = php_array_user_compare_unstable;
5194 fci_data = &fci1;
5195 fci_data_cache = &fci1_cache;
5196 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5197 /* array_intersect_uassoc() or array_intersect_ukey() */
5198 param_spec = "+f";
5199 intersect_key_compare_func = php_array_user_key_compare_unstable;
5200 intersect_data_compare_func = php_array_data_compare_string_unstable;
5201 fci_key = &fci1;
5202 fci_key_cache = &fci1_cache;
5203 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5204 /* array_uintersect_uassoc() */
5205 param_spec = "+ff";
5206 intersect_key_compare_func = php_array_user_key_compare_unstable;
5207 intersect_data_compare_func = php_array_user_compare_unstable;
5208 fci_data = &fci1;
5209 fci_data_cache = &fci1_cache;
5210 fci_key = &fci2;
5211 fci_key_cache = &fci2_cache;
5212 } else {
5213 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5214 return;
5215 }
5216
5217 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5218 RETURN_THROWS();
5219 }
5220
5221 } else {
5222 ZEND_ASSERT(0 && "Invalid behavior");
5223 return;
5224 }
5225
5226 PHP_ARRAY_CMP_FUNC_BACKUP();
5227
5228 /* for each argument, create and sort list with pointers to the hash buckets */
5229 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5230 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5231
5232 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5233 BG(user_compare_fci) = *fci_data;
5234 BG(user_compare_fci_cache) = *fci_data_cache;
5235 } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5236 BG(user_compare_fci) = *fci_key;
5237 BG(user_compare_fci_cache) = *fci_key_cache;
5238 }
5239
5240 for (i = 0; i < arr_argc; i++) {
5241 if (Z_TYPE(args[i]) != IS_ARRAY) {
5242 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5243 arr_argc = i; /* only free up to i - 1 */
5244 goto out;
5245 }
5246 hash = Z_ARRVAL(args[i]);
5247 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5248 lists[i] = list;
5249 ptrs[i] = list;
5250 if (HT_IS_PACKED(hash)) {
5251 zval *zv = hash->arPacked;
5252 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5253 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5254 ZVAL_COPY_VALUE(&list->val, zv);
5255 list->h = idx;
5256 list->key = NULL;
5257 list++;
5258 }
5259 } else {
5260 p = hash->arData;
5261 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5262 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5263 *list++ = *p;
5264 }
5265 }
5266 ZVAL_UNDEF(&list->val);
5267 if (hash->nNumOfElements > 1) {
5268 if (behavior == INTERSECT_NORMAL) {
5269 zend_sort((void *) lists[i], hash->nNumOfElements,
5270 sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5271 (swap_func_t)zend_hash_bucket_swap);
5272 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5273 zend_sort((void *) lists[i], hash->nNumOfElements,
5274 sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5275 (swap_func_t)zend_hash_bucket_swap);
5276 }
5277 }
5278 }
5279
5280 /* copy the argument array if necessary */
5281 in_place = zend_may_modify_arg_in_place(&args[0]);
5282 if (in_place) {
5283 RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5284 } else {
5285 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5286 }
5287
5288 /* go through the lists and look for common values */
5289 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5290 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5291 && key_compare_type == INTERSECT_COMP_KEY_USER) {
5292 BG(user_compare_fci) = *fci_key;
5293 BG(user_compare_fci_cache) = *fci_key_cache;
5294 }
5295
5296 for (i = 1; i < arr_argc; i++) {
5297 if (behavior & INTERSECT_NORMAL) {
5298 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5299 ptrs[i]++;
5300 }
5301 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5302 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5303 ptrs[i]++;
5304 }
5305 if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5306 /* this means that ptrs[i] is not NULL so we can compare
5307 * and "c==0" is from last operation
5308 * in this branch of code we enter only when INTERSECT_ASSOC
5309 * since when we have INTERSECT_KEY compare of data is not wanted. */
5310 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5311 BG(user_compare_fci) = *fci_data;
5312 BG(user_compare_fci_cache) = *fci_data_cache;
5313 }
5314 if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5315 c = 1;
5316 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5317 BG(user_compare_fci) = *fci_key;
5318 BG(user_compare_fci_cache) = *fci_key_cache;
5319 /* When KEY_USER, the last parameter is always the callback */
5320 }
5321 /* we are going to the break */
5322 } else {
5323 /* continue looping */
5324 }
5325 }
5326 }
5327 if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5328 /* delete any values corresponding to remains of ptrs[0] */
5329 /* and exit because they do not present in at least one of */
5330 /* the other arguments */
5331 for (;;) {
5332 p = ptrs[0]++;
5333 if (Z_TYPE(p->val) == IS_UNDEF) {
5334 goto out;
5335 }
5336 if (p->key == NULL) {
5337 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5338 } else {
5339 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5340 }
5341 }
5342 }
5343 if (c) /* here we get if not all are equal */
5344 break;
5345 ptrs[i]++;
5346 }
5347 if (c) {
5348 /* Value of ptrs[0] not in all arguments, delete all entries */
5349 /* with value < value of ptrs[i] */
5350 for (;;) {
5351 p = ptrs[0];
5352 if (p->key == NULL) {
5353 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5354 } else {
5355 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5356 }
5357 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5358 goto out;
5359 }
5360 if (behavior == INTERSECT_NORMAL) {
5361 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5362 break;
5363 }
5364 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5365 /* no need of looping because indexes are unique */
5366 break;
5367 }
5368 }
5369 } else {
5370 /* ptrs[0] is present in all the arguments */
5371 /* Skip all entries with same value as ptrs[0] */
5372 for (;;) {
5373 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5374 goto out;
5375 }
5376 if (behavior == INTERSECT_NORMAL) {
5377 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5378 break;
5379 }
5380 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5381 /* no need of looping because indexes are unique */
5382 break;
5383 }
5384 }
5385 }
5386 }
5387 out:
5388 for (i = 0; i < arr_argc; i++) {
5389 hash = Z_ARRVAL(args[i]);
5390 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5391 }
5392
5393 PHP_ARRAY_CMP_FUNC_RESTORE();
5394
5395 efree(ptrs);
5396 efree(lists);
5397
5398 if (in_place) {
5399 Z_ADDREF_P(return_value);
5400 }
5401 }
5402 /* }}} */
5403
5404 /* {{{ 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. */
5405 PHP_FUNCTION(array_intersect_key)
5406 {
5407 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5408 }
5409 /* }}} */
5410
5411 /* {{{ 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. */
5412 PHP_FUNCTION(array_intersect_ukey)
5413 {
5414 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5415 }
5416 /* }}} */
5417
5418 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5419 PHP_FUNCTION(array_intersect)
5420 {
5421 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5422 }
5423 /* }}} */
5424
5425 /* {{{ 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. */
5426 PHP_FUNCTION(array_uintersect)
5427 {
5428 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5429 }
5430 /* }}} */
5431
5432 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5433 PHP_FUNCTION(array_intersect_assoc)
5434 {
5435 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5436 }
5437 /* }}} */
5438
5439 /* {{{ 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. */
5440 PHP_FUNCTION(array_intersect_uassoc)
5441 {
5442 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5443 }
5444 /* }}} */
5445
5446 /* {{{ 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. */
5447 PHP_FUNCTION(array_uintersect_assoc)
5448 {
5449 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5450 }
5451 /* }}} */
5452
5453 /* {{{ 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. */
5454 PHP_FUNCTION(array_uintersect_uassoc)
5455 {
5456 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5457 }
5458 /* }}} */
5459
5460 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5461 {
5462 uint32_t argc, i;
5463 zval *args;
5464 int (*diff_data_compare_func)(zval *, zval *) = NULL;
5465 bool ok;
5466 zval *val, *data;
5467 zend_string *key;
5468 zend_ulong h;
5469
5470 /* Get the argument count */
5471 argc = ZEND_NUM_ARGS();
5472 if (data_compare_type == DIFF_COMP_DATA_USER) {
5473 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5474 RETURN_THROWS();
5475 }
5476 diff_data_compare_func = zval_user_compare;
5477 } else {
5478 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5479 RETURN_THROWS();
5480 }
5481 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5482 diff_data_compare_func = zval_compare;
5483 }
5484 }
5485
5486 for (i = 0; i < argc; i++) {
5487 if (Z_TYPE(args[i]) != IS_ARRAY) {
5488 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5489 RETURN_THROWS();
5490 }
5491 }
5492
5493 array_init(return_value);
5494
5495 /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5496 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5497 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5498 val = Z_REFVAL_P(val);
5499 }
5500 if (key == NULL) {
5501 ok = 1;
5502 for (i = 1; i < argc; i++) {
5503 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5504 (!diff_data_compare_func ||
5505 diff_data_compare_func(val, data) == 0)
5506 ) {
5507 ok = 0;
5508 break;
5509 }
5510 }
5511 if (ok) {
5512 Z_TRY_ADDREF_P(val);
5513 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5514 }
5515 } else {
5516 ok = 1;
5517 for (i = 1; i < argc; i++) {
5518 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5519 (!diff_data_compare_func ||
5520 diff_data_compare_func(val, data) == 0)
5521 ) {
5522 ok = 0;
5523 break;
5524 }
5525 }
5526 if (ok) {
5527 Z_TRY_ADDREF_P(val);
5528 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5529 }
5530 }
5531 } ZEND_HASH_FOREACH_END();
5532 }
5533 /* }}} */
5534
5535 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5536 {
5537 zval *args = NULL;
5538 HashTable *hash;
5539 uint32_t arr_argc, i;
5540 int c;
5541 uint32_t idx;
5542 Bucket **lists, *list, **ptrs, *p;
5543 char *param_spec;
5544 zend_fcall_info fci1, fci2;
5545 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5546 zend_fcall_info *fci_key = NULL, *fci_data;
5547 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5548 PHP_ARRAY_CMP_FUNC_VARS;
5549
5550 bucket_compare_func_t diff_key_compare_func;
5551 bucket_compare_func_t diff_data_compare_func;
5552
5553 if (behavior == DIFF_NORMAL) {
5554 diff_key_compare_func = php_array_key_compare_string_unstable;
5555
5556 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5557 /* array_diff */
5558 param_spec = "+";
5559 diff_data_compare_func = php_array_data_compare_string_unstable;
5560 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5561 /* array_udiff */
5562 param_spec = "+f";
5563 diff_data_compare_func = php_array_user_compare_unstable;
5564 } else {
5565 ZEND_ASSERT(0 && "Invalid data_compare_type");
5566 return;
5567 }
5568
5569 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5570 RETURN_THROWS();
5571 }
5572 fci_data = &fci1;
5573 fci_data_cache = &fci1_cache;
5574
5575 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5576 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5577 * no comparison of the data is done (part of DIFF_ASSOC) */
5578
5579 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5580 /* array_diff_assoc() or array_diff_key() */
5581 param_spec = "+";
5582 diff_key_compare_func = php_array_key_compare_string_unstable;
5583 diff_data_compare_func = php_array_data_compare_string_unstable;
5584 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5585 /* array_udiff_assoc() */
5586 param_spec = "+f";
5587 diff_key_compare_func = php_array_key_compare_string_unstable;
5588 diff_data_compare_func = php_array_user_compare_unstable;
5589 fci_data = &fci1;
5590 fci_data_cache = &fci1_cache;
5591 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5592 /* array_diff_uassoc() or array_diff_ukey() */
5593 param_spec = "+f";
5594 diff_key_compare_func = php_array_user_key_compare_unstable;
5595 diff_data_compare_func = php_array_data_compare_string_unstable;
5596 fci_key = &fci1;
5597 fci_key_cache = &fci1_cache;
5598 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5599 /* array_udiff_uassoc() */
5600 param_spec = "+ff";
5601 diff_key_compare_func = php_array_user_key_compare_unstable;
5602 diff_data_compare_func = php_array_user_compare_unstable;
5603 fci_data = &fci1;
5604 fci_data_cache = &fci1_cache;
5605 fci_key = &fci2;
5606 fci_key_cache = &fci2_cache;
5607 } else {
5608 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5609 return;
5610 }
5611
5612 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5613 RETURN_THROWS();
5614 }
5615
5616 } else {
5617 ZEND_ASSERT(0 && "Invalid behavior");
5618 return;
5619 }
5620
5621 PHP_ARRAY_CMP_FUNC_BACKUP();
5622
5623 /* for each argument, create and sort list with pointers to the hash buckets */
5624 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5625 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5626
5627 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5628 BG(user_compare_fci) = *fci_data;
5629 BG(user_compare_fci_cache) = *fci_data_cache;
5630 } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5631 BG(user_compare_fci) = *fci_key;
5632 BG(user_compare_fci_cache) = *fci_key_cache;
5633 }
5634
5635 for (i = 0; i < arr_argc; i++) {
5636 if (Z_TYPE(args[i]) != IS_ARRAY) {
5637 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5638 arr_argc = i; /* only free up to i - 1 */
5639 goto out;
5640 }
5641 hash = Z_ARRVAL(args[i]);
5642 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5643 lists[i] = list;
5644 ptrs[i] = list;
5645 if (HT_IS_PACKED(hash)) {
5646 zval *zv = hash->arPacked;
5647 for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5648 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5649 ZVAL_COPY_VALUE(&list->val, zv);
5650 list->h = idx;
5651 list->key = NULL;
5652 list++;
5653 }
5654 } else {
5655 p = hash->arData;
5656 for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5657 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5658 *list++ = *p;
5659 }
5660 }
5661 ZVAL_UNDEF(&list->val);
5662 if (hash->nNumOfElements > 1) {
5663 if (behavior == DIFF_NORMAL) {
5664 zend_sort((void *) lists[i], hash->nNumOfElements,
5665 sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5666 (swap_func_t)zend_hash_bucket_swap);
5667 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5668 zend_sort((void *) lists[i], hash->nNumOfElements,
5669 sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5670 (swap_func_t)zend_hash_bucket_swap);
5671 }
5672 }
5673 }
5674
5675 /* copy the argument array */
5676 RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5677
5678 /* go through the lists and look for values of ptr[0] that are not in the others */
5679 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5680 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5681 &&
5682 key_compare_type == DIFF_COMP_KEY_USER
5683 ) {
5684 BG(user_compare_fci) = *fci_key;
5685 BG(user_compare_fci_cache) = *fci_key_cache;
5686 }
5687 c = 1;
5688 for (i = 1; i < arr_argc; i++) {
5689 Bucket *ptr = ptrs[i];
5690 if (behavior == DIFF_NORMAL) {
5691 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5692 ptrs[i]++;
5693 }
5694 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5695 while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5696 ptr++;
5697 }
5698 }
5699 if (!c) {
5700 if (behavior == DIFF_NORMAL) {
5701 if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5702 ptrs[i]++;
5703 }
5704 break;
5705 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
5706 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5707 * data comparison is not needed - skipped. */
5708 if (Z_TYPE(ptr->val) != IS_UNDEF) {
5709 if (data_compare_type == DIFF_COMP_DATA_USER) {
5710 BG(user_compare_fci) = *fci_data;
5711 BG(user_compare_fci_cache) = *fci_data_cache;
5712 }
5713 if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5714 /* the data is not the same */
5715 c = -1;
5716 if (key_compare_type == DIFF_COMP_KEY_USER) {
5717 BG(user_compare_fci) = *fci_key;
5718 BG(user_compare_fci_cache) = *fci_key_cache;
5719 }
5720 } else {
5721 break;
5722 /* we have found the element in other arrays thus we don't want it
5723 * in the return_value -> delete from there */
5724 }
5725 }
5726 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5727 /* the behavior here differs from INTERSECT_KEY in php_intersect
5728 * since in the "diff" case we have to remove the entry from
5729 * return_value while when doing intersection the entry must not
5730 * be deleted. */
5731 break; /* remove the key */
5732 }
5733 }
5734 }
5735 if (!c) {
5736 /* ptrs[0] in one of the other arguments */
5737 /* delete all entries with value as ptrs[0] */
5738 for (;;) {
5739 p = ptrs[0];
5740 if (p->key == NULL) {
5741 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5742 } else {
5743 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5744 }
5745 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5746 goto out;
5747 }
5748 if (behavior == DIFF_NORMAL) {
5749 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5750 break;
5751 }
5752 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5753 /* in this case no array_key_compare is needed */
5754 break;
5755 }
5756 }
5757 } else {
5758 /* ptrs[0] in none of the other arguments */
5759 /* skip all entries with value as ptrs[0] */
5760 for (;;) {
5761 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5762 goto out;
5763 }
5764 if (behavior == DIFF_NORMAL) {
5765 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5766 break;
5767 }
5768 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5769 /* in this case no array_key_compare is needed */
5770 break;
5771 }
5772 }
5773 }
5774 }
5775 out:
5776 for (i = 0; i < arr_argc; i++) {
5777 hash = Z_ARRVAL(args[i]);
5778 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5779 }
5780
5781 PHP_ARRAY_CMP_FUNC_RESTORE();
5782
5783 efree(ptrs);
5784 efree(lists);
5785 }
5786 /* }}} */
5787
5788 /* {{{ 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. */
5789 PHP_FUNCTION(array_diff_key)
5790 {
5791 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5792 }
5793 /* }}} */
5794
5795 /* {{{ 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. */
5796 PHP_FUNCTION(array_diff_ukey)
5797 {
5798 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5799 }
5800 /* }}} */
5801
5802 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5803 PHP_FUNCTION(array_diff)
5804 {
5805 zval *args;
5806 uint32_t argc, i;
5807 uint32_t num;
5808 HashTable exclude;
5809 zval *value;
5810 zend_string *str, *tmp_str, *key;
5811 zend_long idx;
5812 zval dummy;
5813
5814 ZEND_PARSE_PARAMETERS_START(1, -1)
5815 Z_PARAM_VARIADIC('+', args, argc)
5816 ZEND_PARSE_PARAMETERS_END();
5817
5818 if (Z_TYPE(args[0]) != IS_ARRAY) {
5819 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5820 RETURN_THROWS();
5821 }
5822
5823 num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5824 if (num == 0) {
5825 for (i = 1; i < argc; i++) {
5826 if (Z_TYPE(args[i]) != IS_ARRAY) {
5827 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5828 RETURN_THROWS();
5829 }
5830 }
5831 RETURN_EMPTY_ARRAY();
5832 } else if (num == 1) {
5833 int found = 0;
5834 zend_string *search_str, *tmp_search_str;
5835
5836 value = NULL;
5837 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5838 break;
5839 } ZEND_HASH_FOREACH_END();
5840
5841 if (!value) {
5842 for (i = 1; i < argc; i++) {
5843 if (Z_TYPE(args[i]) != IS_ARRAY) {
5844 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5845 RETURN_THROWS();
5846 }
5847 }
5848 RETURN_EMPTY_ARRAY();
5849 }
5850
5851 search_str = zval_get_tmp_string(value, &tmp_search_str);
5852
5853 for (i = 1; i < argc; i++) {
5854 if (Z_TYPE(args[i]) != IS_ARRAY) {
5855 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5856 RETURN_THROWS();
5857 }
5858 if (!found) {
5859 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5860 str = zval_get_tmp_string(value, &tmp_str);
5861 if (zend_string_equals(search_str, str)) {
5862 zend_tmp_string_release(tmp_str);
5863 found = 1;
5864 break;
5865 }
5866 zend_tmp_string_release(tmp_str);
5867 } ZEND_HASH_FOREACH_END();
5868 }
5869 }
5870
5871 zend_tmp_string_release(tmp_search_str);
5872
5873 if (found) {
5874 RETVAL_EMPTY_ARRAY();
5875 } else {
5876 ZVAL_COPY(return_value, &args[0]);
5877 }
5878 return;
5879 }
5880
5881 /* count number of elements */
5882 num = 0;
5883 for (i = 1; i < argc; i++) {
5884 if (Z_TYPE(args[i]) != IS_ARRAY) {
5885 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5886 RETURN_THROWS();
5887 }
5888 num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5889 }
5890
5891 if (num == 0) {
5892 ZVAL_COPY(return_value, &args[0]);
5893 return;
5894 }
5895
5896 ZVAL_NULL(&dummy);
5897 /* create exclude map */
5898 zend_hash_init(&exclude, num, NULL, NULL, 0);
5899 for (i = 1; i < argc; i++) {
5900 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5901 str = zval_get_tmp_string(value, &tmp_str);
5902 zend_hash_add(&exclude, str, &dummy);
5903 zend_tmp_string_release(tmp_str);
5904 } ZEND_HASH_FOREACH_END();
5905 }
5906
5907 /* copy all elements of first array that are not in exclude set */
5908 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5909 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5910 str = zval_get_tmp_string(value, &tmp_str);
5911 if (!zend_hash_exists(&exclude, str)) {
5912 if (key) {
5913 value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5914 } else {
5915 value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5916 }
5917 zval_add_ref(value);
5918 }
5919 zend_tmp_string_release(tmp_str);
5920 } ZEND_HASH_FOREACH_END();
5921
5922 zend_hash_destroy(&exclude);
5923 }
5924 /* }}} */
5925
5926 /* {{{ 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. */
5927 PHP_FUNCTION(array_udiff)
5928 {
5929 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5930 }
5931 /* }}} */
5932
5933 /* {{{ 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 */
5934 PHP_FUNCTION(array_diff_assoc)
5935 {
5936 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5937 }
5938 /* }}} */
5939
5940 /* {{{ 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. */
5941 PHP_FUNCTION(array_diff_uassoc)
5942 {
5943 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5944 }
5945 /* }}} */
5946
5947 /* {{{ 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. */
5948 PHP_FUNCTION(array_udiff_assoc)
5949 {
5950 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5951 }
5952 /* }}} */
5953
5954 /* {{{ 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. */
5955 PHP_FUNCTION(array_udiff_uassoc)
5956 {
5957 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5958 }
5959 /* }}} */
5960
5961 #define MULTISORT_ORDER 0
5962 #define MULTISORT_TYPE 1
5963 #define MULTISORT_LAST 2
5964
5965 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5966 {
5967 Bucket *ab = *(Bucket **)a;
5968 Bucket *bb = *(Bucket **)b;
5969 int r;
5970 zend_long result;
5971
5972 r = 0;
5973 do {
5974 result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5975 if (result != 0) {
5976 return result > 0 ? 1 : -1;
5977 }
5978 r++;
5979 } while (Z_TYPE(ab[r].val) != IS_UNDEF);
5980
5981 return stable_sort_fallback(&ab[r], &bb[r]);
5982 }
5983 /* }}} */
5984
5985 #define MULTISORT_ABORT \
5986 efree(func); \
5987 efree(arrays); \
5988 return;
5989
5990 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5991 Bucket *t;
5992 Bucket **f = (Bucket **)p;
5993 Bucket **g = (Bucket **)q;
5994
5995 t = *f;
5996 *f = *g;
5997 *g = t;
5998 }
5999 /* }}} */
6000
6001 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
6002 PHP_FUNCTION(array_multisort)
6003 {
6004 zval* args;
6005 zval** arrays;
6006 Bucket** indirect;
6007 uint32_t idx;
6008 HashTable* hash;
6009 uint32_t argc;
6010 uint32_t array_size;
6011 uint32_t num_arrays = 0;
6012 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
6013 int sort_order = PHP_SORT_ASC;
6014 int sort_type = PHP_SORT_REGULAR;
6015 uint32_t i, k, n;
6016 bucket_compare_func_t *func;
6017
6018 ZEND_PARSE_PARAMETERS_START(1, -1)
6019 Z_PARAM_VARIADIC('+', args, argc)
6020 ZEND_PARSE_PARAMETERS_END();
6021
6022 /* Allocate space for storing pointers to input arrays and sort flags. */
6023 arrays = (zval **)ecalloc(argc, sizeof(zval *));
6024 for (i = 0; i < MULTISORT_LAST; i++) {
6025 parse_state[i] = 0;
6026 }
6027 func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
6028
6029 /* Here we go through the input arguments and parse them. Each one can
6030 * be either an array or a sort flag which follows an array. If not
6031 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
6032 * accordingly. There can't be two sort flags of the same type after an
6033 * array, and the very first argument has to be an array. */
6034 for (i = 0; i < argc; i++) {
6035 zval *arg = &args[i];
6036
6037 ZVAL_DEREF(arg);
6038 if (Z_TYPE_P(arg) == IS_ARRAY) {
6039 SEPARATE_ARRAY(arg);
6040 /* We see the next array, so we update the sort flags of
6041 * the previous array and reset the sort flags. */
6042 if (i > 0) {
6043 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6044 sort_order = PHP_SORT_ASC;
6045 sort_type = PHP_SORT_REGULAR;
6046 }
6047 arrays[num_arrays++] = arg;
6048
6049 /* Next one may be an array or a list of sort flags. */
6050 for (k = 0; k < MULTISORT_LAST; k++) {
6051 parse_state[k] = 1;
6052 }
6053 } else if (Z_TYPE_P(arg) == IS_LONG) {
6054 switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
6055 case PHP_SORT_ASC:
6056 case PHP_SORT_DESC:
6057 /* flag allowed here */
6058 if (parse_state[MULTISORT_ORDER] == 1) {
6059 /* Save the flag and make sure then next arg is not the current flag. */
6060 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
6061 parse_state[MULTISORT_ORDER] = 0;
6062 } else {
6063 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6064 MULTISORT_ABORT;
6065 }
6066 break;
6067
6068 case PHP_SORT_REGULAR:
6069 case PHP_SORT_NUMERIC:
6070 case PHP_SORT_STRING:
6071 case PHP_SORT_NATURAL:
6072 case PHP_SORT_LOCALE_STRING:
6073 /* flag allowed here */
6074 if (parse_state[MULTISORT_TYPE] == 1) {
6075 /* Save the flag and make sure then next arg is not the current flag. */
6076 sort_type = (int)Z_LVAL_P(arg);
6077 parse_state[MULTISORT_TYPE] = 0;
6078 } else {
6079 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6080 MULTISORT_ABORT;
6081 }
6082 break;
6083
6084 default:
6085 zend_argument_value_error(i + 1, "must be a valid sort flag");
6086 MULTISORT_ABORT;
6087 break;
6088
6089 }
6090 } else {
6091 zend_argument_type_error(i + 1, "must be an array or a sort flag");
6092 MULTISORT_ABORT;
6093 }
6094 }
6095 /* Take care of the last array sort flags. */
6096 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6097
6098 /* Make sure the arrays are of the same size. */
6099 array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6100 for (i = 1; i < num_arrays; i++) {
6101 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
6102 zend_value_error("Array sizes are inconsistent");
6103 MULTISORT_ABORT;
6104 }
6105 }
6106
6107 /* If all arrays are empty we don't need to do anything. */
6108 if (array_size < 1) {
6109 efree(func);
6110 efree(arrays);
6111 RETURN_TRUE;
6112 }
6113
6114 /* Create the indirection array. This array is of size MxN, where
6115 * M is the number of entries in each input array and N is the number
6116 * of the input arrays + 1. The last column is UNDEF to indicate the end
6117 * of the row. It also stores the original position for stable sorting. */
6118 indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6119 /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6120 Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6121 for (i = 0; i < array_size; i++) {
6122 indirect[i] = indirects + (i * (num_arrays + 1));
6123 }
6124 for (i = 0; i < num_arrays; i++) {
6125 k = 0;
6126 if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6127 zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6128 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6129 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6130 ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6131 indirect[k][i].h = idx;
6132 indirect[k][i].key = NULL;
6133 k++;
6134 }
6135 } else {
6136 Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6137 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6138 if (Z_TYPE(p->val) == IS_UNDEF) continue;
6139 indirect[k][i] = *p;
6140 k++;
6141 }
6142 }
6143 }
6144 for (k = 0; k < array_size; k++) {
6145 ZVAL_UNDEF(&indirect[k][num_arrays].val);
6146 Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6147 }
6148
6149 /* Do the actual sort magic - bada-bim, bada-boom. */
6150 zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6151 if (EG(exception)) {
6152 goto clean_up;
6153 }
6154
6155 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6156 for (i = 0; i < num_arrays; i++) {
6157 hash = Z_ARRVAL_P(arrays[i]);
6158 hash->nNumUsed = array_size;
6159 hash->nNextFreeElement = array_size;
6160 hash->nInternalPointer = 0;
6161 if (HT_IS_PACKED(hash)) {
6162 for (k = 0; k < array_size; k++) {
6163 ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6164 }
6165 } else {
6166 bool repack = true;
6167
6168 for (n = 0, k = 0; k < array_size; k++) {
6169 hash->arData[k] = indirect[k][i];
6170 if (hash->arData[k].key == NULL) {
6171 hash->arData[k].h = n++;
6172 } else {
6173 repack = false;
6174 }
6175 }
6176 if (repack) {
6177 zend_hash_to_packed(hash);
6178 } else {
6179 zend_hash_rehash(hash);
6180 }
6181 }
6182 }
6183 RETVAL_TRUE;
6184
6185 clean_up:
6186 efree(indirects);
6187 efree(indirect);
6188 efree(func);
6189 efree(arrays);
6190 }
6191 /* }}} */
6192
6193 /* {{{ php_array_pick_keys */
6194 PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent)
6195 {
6196 HashTable *ht = Z_ARRVAL_P(input);
6197 uint32_t num_avail = zend_hash_num_elements(ht);
6198 zend_long i, randval;
6199 zend_string *string_key;
6200 zend_ulong num_key;
6201 zval *zv;
6202 Bucket *b;
6203 zend_bitset bitset;
6204 int negative_bitset = 0;
6205 uint32_t bitset_len;
6206 ALLOCA_FLAG(use_heap);
6207
6208 if (num_avail == 0) {
6209 if (!silent) {
6210 zend_argument_value_error(1, "cannot be empty");
6211 }
6212 return false;
6213 }
6214
6215 if (num_req == 1) {
6216 if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6217 /* If less than 1/2 of elements are used, don't sample. Instead search for a
6218 * specific offset using linear scan. */
6219 i = 0;
6220 randval = algo->range(status, 0, num_avail - 1);
6221 if (EG(exception)) {
6222 return false;
6223 }
6224 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6225 if (i == randval) {
6226 if (string_key) {
6227 ZVAL_STR_COPY(retval, string_key);
6228 } else {
6229 ZVAL_LONG(retval, num_key);
6230 }
6231 return true;
6232 }
6233 i++;
6234 } ZEND_HASH_FOREACH_END();
6235 }
6236
6237 /* Sample random buckets until we hit one that is not empty.
6238 * The worst case probability of hitting an empty element is 1-1/2. The worst case
6239 * probability of hitting N empty elements in a row is (1-1/2)**N.
6240 * For N=10 this becomes smaller than 0.1%. */
6241 if (HT_IS_PACKED(ht)) {
6242 do {
6243 randval = algo->range(status, 0, ht->nNumUsed - 1);
6244 if (EG(exception)) {
6245 return false;
6246 }
6247 zv = &ht->arPacked[randval];
6248 if (!Z_ISUNDEF_P(zv)) {
6249 ZVAL_LONG(retval, randval);
6250 return true;
6251 }
6252 } while (true);
6253 } else {
6254 do {
6255 randval = algo->range(status, 0, ht->nNumUsed - 1);
6256 if (EG(exception)) {
6257 return false;
6258 }
6259 b = &ht->arData[randval];
6260 if (!Z_ISUNDEF(b->val)) {
6261 if (b->key) {
6262 ZVAL_STR_COPY(retval, b->key);
6263 } else {
6264 ZVAL_LONG(retval, b->h);
6265 }
6266 return true;
6267 }
6268 } while (true);
6269 }
6270 }
6271
6272 if (num_req <= 0 || num_req > num_avail) {
6273 if (!silent) {
6274 zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6275 }
6276 return false;
6277 }
6278
6279 /* Make the return value an array only if we need to pass back more than one result. */
6280 array_init_size(retval, (uint32_t) num_req);
6281 if (num_req > (num_avail >> 1)) {
6282 negative_bitset = 1;
6283 num_req = num_avail - num_req;
6284 }
6285
6286 bitset_len = zend_bitset_len(num_avail);
6287 bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6288 zend_bitset_clear(bitset, bitset_len);
6289
6290 i = num_req;
6291 int failures = 0;
6292 while (i) {
6293 randval = algo->range(status, 0, num_avail - 1);
6294 if (EG(exception)) {
6295 goto fail;
6296 }
6297 if (zend_bitset_in(bitset, randval)) {
6298 if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6299 if (!silent) {
6300 zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6301 }
6302
6303 goto fail;
6304 }
6305 } else {
6306 zend_bitset_incl(bitset, randval);
6307 i--;
6308 failures = 0;
6309 }
6310 }
6311
6312 zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6313 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6314 /* We can't use zend_hash_index_find()
6315 * because the array may have string keys or gaps. */
6316 ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6317 if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6318 if (string_key) {
6319 ZEND_HASH_FILL_SET_STR_COPY(string_key);
6320 } else {
6321 ZEND_HASH_FILL_SET_LONG(num_key);
6322 }
6323 ZEND_HASH_FILL_NEXT();
6324 }
6325 i++;
6326 } ZEND_HASH_FOREACH_END();
6327 } ZEND_HASH_FILL_END();
6328
6329 free_alloca(bitset, use_heap);
6330
6331 return true;
6332
6333 fail:
6334 free_alloca(bitset, use_heap);
6335
6336 return false;
6337 }
6338 /* }}} */
6339
6340 /* {{{ Return key/keys for random entry/entries in the array */
6341 PHP_FUNCTION(array_rand)
6342 {
6343 zval *input;
6344 zend_long num_req = 1;
6345
6346 ZEND_PARSE_PARAMETERS_START(1, 2)
6347 Z_PARAM_ARRAY(input)
6348 Z_PARAM_OPTIONAL
6349 Z_PARAM_LONG(num_req)
6350 ZEND_PARSE_PARAMETERS_END();
6351
6352 if (!php_array_pick_keys(
6353 php_random_default_algo(),
6354 php_random_default_status(),
6355 input,
6356 num_req,
6357 return_value,
6358 false)
6359 ) {
6360 RETURN_THROWS();
6361 }
6362 }
6363 /* }}} */
6364
6365 /* Wrapper for array_sum and array_product */
6366 static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6367 {
6368 HashTable *input;
6369 zval *entry;
6370
6371 ZEND_PARSE_PARAMETERS_START(1, 1)
6372 Z_PARAM_ARRAY_HT(input)
6373 ZEND_PARSE_PARAMETERS_END();
6374
6375 if (zend_hash_num_elements(input) == 0) {
6376 RETURN_LONG(initial);
6377 }
6378
6379 ZVAL_LONG(return_value, initial);
6380 ZEND_HASH_FOREACH_VAL(input, entry) {
6381 /* For objects we try to cast them to a numeric type */
6382 if (Z_TYPE_P(entry) == IS_OBJECT) {
6383 zval dst;
6384 zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6385
6386 /* Do not type error for BC */
6387 if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6388 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6389 op_name, zend_zval_type_name(entry));
6390 continue;
6391 }
6392 op(return_value, return_value, &dst);
6393 continue;
6394 }
6395
6396 zend_result status = op(return_value, return_value, entry);
6397 if (status == FAILURE) {
6398 ZEND_ASSERT(EG(exception));
6399 zend_clear_exception();
6400 /* BC resources: previously resources were cast to int */
6401 if (Z_TYPE_P(entry) == IS_RESOURCE) {
6402 zval tmp;
6403 ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6404 op(return_value, return_value, &tmp);
6405 }
6406 /* BC non numeric strings: previously were cast to 0 */
6407 else if (Z_TYPE_P(entry) == IS_STRING) {
6408 zval tmp;
6409 ZVAL_LONG(&tmp, 0);
6410 op(return_value, return_value, &tmp);
6411 }
6412 php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6413 op_name, zend_zval_type_name(entry));
6414 }
6415 } ZEND_HASH_FOREACH_END();
6416 }
6417
6418 /* {{{ Returns the sum of the array entries */
6419 PHP_FUNCTION(array_sum)
6420 {
6421 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6422 }
6423 /* }}} */
6424
6425 /* {{{ Returns the product of the array entries */
6426 PHP_FUNCTION(array_product)
6427 {
6428 php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6429 }
6430 /* }}} */
6431
6432 /* {{{ Iteratively reduce the array to a single value via the callback. */
6433 PHP_FUNCTION(array_reduce)
6434 {
6435 zval *input;
6436 zval args[2];
6437 zval *operand;
6438 zval retval;
6439 zend_fcall_info fci;
6440 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6441 zval *initial = NULL;
6442 HashTable *htbl;
6443
6444 ZEND_PARSE_PARAMETERS_START(2, 3)
6445 Z_PARAM_ARRAY(input)
6446 Z_PARAM_FUNC(fci, fci_cache)
6447 Z_PARAM_OPTIONAL
6448 Z_PARAM_ZVAL(initial)
6449 ZEND_PARSE_PARAMETERS_END();
6450
6451
6452 if (ZEND_NUM_ARGS() > 2) {
6453 ZVAL_COPY(return_value, initial);
6454 } else {
6455 ZVAL_NULL(return_value);
6456 }
6457
6458 /* (zval **)input points to an element of argument stack
6459 * the base pointer of which is subject to change.
6460 * thus we need to keep the pointer to the hashtable for safety */
6461 htbl = Z_ARRVAL_P(input);
6462
6463 if (zend_hash_num_elements(htbl) == 0) {
6464 return;
6465 }
6466
6467 fci.retval = &retval;
6468 fci.param_count = 2;
6469
6470 ZEND_HASH_FOREACH_VAL(htbl, operand) {
6471 ZVAL_COPY_VALUE(&args[0], return_value);
6472 ZVAL_COPY(&args[1], operand);
6473 fci.params = args;
6474
6475 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6476 zval_ptr_dtor(&args[1]);
6477 zval_ptr_dtor(&args[0]);
6478 ZVAL_COPY_VALUE(return_value, &retval);
6479 if (UNEXPECTED(Z_ISREF_P(return_value))) {
6480 zend_unwrap_reference(return_value);
6481 }
6482 } else {
6483 zval_ptr_dtor(&args[1]);
6484 zval_ptr_dtor(&args[0]);
6485 RETURN_NULL();
6486 }
6487 } ZEND_HASH_FOREACH_END();
6488 }
6489 /* }}} */
6490
6491 /* {{{ Filters elements from the array via the callback. */
6492 PHP_FUNCTION(array_filter)
6493 {
6494 zval *array;
6495 zval *operand;
6496 zval *key;
6497 zval args[2];
6498 zval retval;
6499 bool have_callback = 0;
6500 zend_long use_type = 0;
6501 zend_string *string_key;
6502 zend_fcall_info fci = empty_fcall_info;
6503 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6504 zend_ulong num_key;
6505
6506 ZEND_PARSE_PARAMETERS_START(1, 3)
6507 Z_PARAM_ARRAY(array)
6508 Z_PARAM_OPTIONAL
6509 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6510 Z_PARAM_LONG(use_type)
6511 ZEND_PARSE_PARAMETERS_END();
6512
6513 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6514 RETVAL_EMPTY_ARRAY();
6515 return;
6516 }
6517 array_init(return_value);
6518
6519 if (ZEND_FCI_INITIALIZED(fci)) {
6520 have_callback = 1;
6521 fci.retval = &retval;
6522 if (use_type == ARRAY_FILTER_USE_BOTH) {
6523 fci.param_count = 2;
6524 key = &args[1];
6525 } else {
6526 fci.param_count = 1;
6527 key = &args[0];
6528 }
6529 }
6530
6531 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6532 if (have_callback) {
6533 if (use_type) {
6534 /* Set up the key */
6535 if (!string_key) {
6536 ZVAL_LONG(key, num_key);
6537 } else {
6538 ZVAL_STR_COPY(key, string_key);
6539 }
6540 }
6541 if (use_type != ARRAY_FILTER_USE_KEY) {
6542 ZVAL_COPY(&args[0], operand);
6543 }
6544 fci.params = args;
6545
6546 if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6547 int retval_true;
6548
6549 zval_ptr_dtor(&args[0]);
6550 if (use_type == ARRAY_FILTER_USE_BOTH) {
6551 zval_ptr_dtor(&args[1]);
6552 }
6553 retval_true = zend_is_true(&retval);
6554 zval_ptr_dtor(&retval);
6555 if (!retval_true) {
6556 continue;
6557 }
6558 } else {
6559 zval_ptr_dtor(&args[0]);
6560 if (use_type == ARRAY_FILTER_USE_BOTH) {
6561 zval_ptr_dtor(&args[1]);
6562 }
6563 return;
6564 }
6565 } else if (!zend_is_true(operand)) {
6566 continue;
6567 }
6568
6569 if (string_key) {
6570 operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6571 } else {
6572 operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6573 }
6574 zval_add_ref(operand);
6575 } ZEND_HASH_FOREACH_END();
6576 }
6577 /* }}} */
6578
6579 /* {{{ Applies the callback to the elements in given arrays. */
6580 PHP_FUNCTION(array_map)
6581 {
6582 zval *arrays = NULL;
6583 int n_arrays = 0;
6584 zval result;
6585 zend_fcall_info fci = empty_fcall_info;
6586 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6587 int i;
6588 uint32_t k, maxlen = 0;
6589
6590 ZEND_PARSE_PARAMETERS_START(2, -1)
6591 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6592 Z_PARAM_VARIADIC('+', arrays, n_arrays)
6593 ZEND_PARSE_PARAMETERS_END();
6594
6595 if (n_arrays == 1) {
6596 zend_ulong num_key;
6597 zend_string *str_key;
6598 zval *zv, arg;
6599 int ret;
6600
6601 if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6602 zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6603 RETURN_THROWS();
6604 }
6605 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6606
6607 /* Short-circuit: if no callback and only one array, just return it. */
6608 if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6609 ZVAL_COPY(return_value, &arrays[0]);
6610 return;
6611 }
6612
6613 array_init_size(return_value, maxlen);
6614 zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6615
6616 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6617 fci.retval = &result;
6618 fci.param_count = 1;
6619 fci.params = &arg;
6620
6621 ZVAL_COPY(&arg, zv);
6622 ret = zend_call_function(&fci, &fci_cache);
6623 i_zval_ptr_dtor(&arg);
6624 if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6625 zend_array_destroy(Z_ARR_P(return_value));
6626 RETURN_NULL();
6627 }
6628 if (str_key) {
6629 _zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6630 } else {
6631 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6632 }
6633 } ZEND_HASH_FOREACH_END();
6634 } else {
6635 uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6636
6637 for (i = 0; i < n_arrays; i++) {
6638 if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6639 zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6640 efree(array_pos);
6641 RETURN_THROWS();
6642 }
6643 if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6644 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6645 }
6646 }
6647
6648 array_init_size(return_value, maxlen);
6649
6650 if (!ZEND_FCI_INITIALIZED(fci)) {
6651 zval zv;
6652
6653 /* We iterate through all the arrays at once. */
6654 for (k = 0; k < maxlen; k++) {
6655
6656 /* If no callback, the result will be an array, consisting of current
6657 * entries from all arrays. */
6658 array_init_size(&result, n_arrays);
6659
6660 for (i = 0; i < n_arrays; i++) {
6661 /* If this array still has elements, add the current one to the
6662 * parameter list, otherwise use null value. */
6663 uint32_t pos = array_pos[i];
6664 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6665 while (1) {
6666 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6667 ZVAL_NULL(&zv);
6668 break;
6669 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6670 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6671 array_pos[i] = pos + 1;
6672 break;
6673 }
6674 pos++;
6675 }
6676 } else {
6677 while (1) {
6678 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6679 ZVAL_NULL(&zv);
6680 break;
6681 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6682 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6683 array_pos[i] = pos + 1;
6684 break;
6685 }
6686 pos++;
6687 }
6688 }
6689 zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6690 }
6691
6692 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6693 }
6694 } else {
6695 zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6696
6697 /* We iterate through all the arrays at once. */
6698 for (k = 0; k < maxlen; k++) {
6699 for (i = 0; i < n_arrays; i++) {
6700 /* If this array still has elements, add the current one to the
6701 * parameter list, otherwise use null value. */
6702 uint32_t pos = array_pos[i];
6703 if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6704 while (1) {
6705 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6706 ZVAL_NULL(¶ms[i]);
6707 break;
6708 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6709 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6710 array_pos[i] = pos + 1;
6711 break;
6712 }
6713 pos++;
6714 }
6715 } else {
6716 while (1) {
6717 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6718 ZVAL_NULL(¶ms[i]);
6719 break;
6720 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6721 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6722 array_pos[i] = pos + 1;
6723 break;
6724 }
6725 pos++;
6726 }
6727 }
6728 }
6729
6730 fci.retval = &result;
6731 fci.param_count = n_arrays;
6732 fci.params = params;
6733
6734 if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6735 efree(array_pos);
6736 zend_array_destroy(Z_ARR_P(return_value));
6737 for (i = 0; i < n_arrays; i++) {
6738 zval_ptr_dtor(¶ms[i]);
6739 }
6740 efree(params);
6741 RETURN_NULL();
6742 } else {
6743 for (i = 0; i < n_arrays; i++) {
6744 zval_ptr_dtor(¶ms[i]);
6745 }
6746 }
6747
6748 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6749 }
6750
6751 efree(params);
6752 }
6753 efree(array_pos);
6754 }
6755 }
6756 /* }}} */
6757
6758 /* {{{ Checks if the given key or index exists in the array */
6759 PHP_FUNCTION(array_key_exists)
6760 {
6761 zval *key;
6762 HashTable *ht;
6763
6764 ZEND_PARSE_PARAMETERS_START(2, 2)
6765 Z_PARAM_ZVAL(key)
6766 Z_PARAM_ARRAY_HT(ht)
6767 ZEND_PARSE_PARAMETERS_END();
6768
6769 switch (Z_TYPE_P(key)) {
6770 case IS_STRING:
6771 RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6772 break;
6773 case IS_LONG:
6774 RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6775 break;
6776 case IS_NULL:
6777 RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6778 break;
6779 case IS_DOUBLE:
6780 RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6781 break;
6782 case IS_FALSE:
6783 RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6784 break;
6785 case IS_TRUE:
6786 RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6787 break;
6788 case IS_RESOURCE:
6789 zend_use_resource_as_offset(key);
6790 RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6791 break;
6792 default:
6793 zend_argument_type_error(1, "must be a valid array offset type");
6794 break;
6795 }
6796 }
6797 /* }}} */
6798
6799 /* {{{ Split array into chunks */
6800 PHP_FUNCTION(array_chunk)
6801 {
6802 int num_in;
6803 zend_long size, current = 0;
6804 zend_string *str_key;
6805 zend_ulong num_key;
6806 bool preserve_keys = 0;
6807 zval *input = NULL;
6808 zval chunk;
6809 zval *entry;
6810
6811 ZEND_PARSE_PARAMETERS_START(2, 3)
6812 Z_PARAM_ARRAY(input)
6813 Z_PARAM_LONG(size)
6814 Z_PARAM_OPTIONAL
6815 Z_PARAM_BOOL(preserve_keys)
6816 ZEND_PARSE_PARAMETERS_END();
6817
6818 /* Do bounds checking for size parameter. */
6819 if (size < 1) {
6820 zend_argument_value_error(2, "must be greater than 0");
6821 RETURN_THROWS();
6822 }
6823
6824 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6825
6826 if (size > num_in) {
6827 if (num_in == 0) {
6828 RETVAL_EMPTY_ARRAY();
6829 return;
6830 }
6831 size = num_in;
6832 }
6833
6834 array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6835
6836 ZVAL_UNDEF(&chunk);
6837
6838 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6839 /* If new chunk, create and initialize it. */
6840 if (Z_TYPE(chunk) == IS_UNDEF) {
6841 array_init_size(&chunk, (uint32_t)size);
6842 }
6843
6844 /* Add entry to the chunk, preserving keys if necessary. */
6845 if (preserve_keys) {
6846 if (str_key) {
6847 entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6848 } else {
6849 entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6850 }
6851 } else {
6852 entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6853 }
6854 zval_add_ref(entry);
6855
6856 /* If reached the chunk size, add it to the result array, and reset the
6857 * pointer. */
6858 if (!(++current % size)) {
6859 add_next_index_zval(return_value, &chunk);
6860 ZVAL_UNDEF(&chunk);
6861 }
6862 } ZEND_HASH_FOREACH_END();
6863
6864 /* Add the final chunk if there is one. */
6865 if (Z_TYPE(chunk) != IS_UNDEF) {
6866 add_next_index_zval(return_value, &chunk);
6867 }
6868 }
6869 /* }}} */
6870
6871 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6872 PHP_FUNCTION(array_combine)
6873 {
6874 HashTable *values, *keys;
6875 uint32_t pos_values = 0;
6876 zval *entry_keys, *entry_values;
6877 int num_keys, num_values;
6878
6879 ZEND_PARSE_PARAMETERS_START(2, 2)
6880 Z_PARAM_ARRAY_HT(keys)
6881 Z_PARAM_ARRAY_HT(values)
6882 ZEND_PARSE_PARAMETERS_END();
6883
6884 num_keys = zend_hash_num_elements(keys);
6885 num_values = zend_hash_num_elements(values);
6886
6887 if (num_keys != num_values) {
6888 zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6889 RETURN_THROWS();
6890 }
6891
6892 if (!num_keys) {
6893 RETURN_EMPTY_ARRAY();
6894 }
6895
6896 array_init_size(return_value, num_keys);
6897 ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6898 while (1) {
6899 if (pos_values >= values->nNumUsed) {
6900 break;
6901 }
6902 entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6903 if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6904 if (Z_TYPE_P(entry_keys) == IS_LONG) {
6905 entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6906 Z_LVAL_P(entry_keys), entry_values);
6907 } else {
6908 zend_string *tmp_key;
6909 zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6910 entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6911 key, entry_values);
6912 zend_tmp_string_release(tmp_key);
6913 }
6914 zval_add_ref(entry_values);
6915 pos_values++;
6916 break;
6917 }
6918 pos_values++;
6919 }
6920 } ZEND_HASH_FOREACH_END();
6921 }
6922 /* }}} */
6923