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