xref: /PHP-8.2/ext/standard/array.c (revision 24a29492)
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_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
709 	} else {
710 		zend_hash_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_hash_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_hash_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_hash_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_hash_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_hash_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 				SEPARATE_ZVAL(dest_entry);
3704 				dest_zval = dest_entry;
3705 
3706 				if (Z_TYPE_P(dest_zval) == IS_NULL) {
3707 					convert_to_array(dest_zval);
3708 					add_next_index_null(dest_zval);
3709 				} else {
3710 					convert_to_array(dest_zval);
3711 				}
3712 				ZVAL_UNDEF(&tmp);
3713 				if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3714 					ZVAL_COPY(&tmp, src_zval);
3715 					convert_to_array(&tmp);
3716 					src_zval = &tmp;
3717 				}
3718 				if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3719 					if (thash) {
3720 						GC_TRY_PROTECT_RECURSION(thash);
3721 					}
3722 					ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3723 					if (thash) {
3724 						GC_TRY_UNPROTECT_RECURSION(thash);
3725 					}
3726 					if (!ret) {
3727 						return 0;
3728 					}
3729 				} else {
3730 					Z_TRY_ADDREF_P(src_zval);
3731 					zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3732 					if (EXPECTED(!zv)) {
3733 						Z_TRY_DELREF_P(src_zval);
3734 						zend_cannot_add_element();
3735 						return 0;
3736 					}
3737 				}
3738 				zval_ptr_dtor(&tmp);
3739 			} else {
3740 				zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3741 				zval_add_ref(zv);
3742 			}
3743 		} else {
3744 			zval *zv = zend_hash_next_index_insert(dest, src_entry);
3745 			if (UNEXPECTED(!zv)) {
3746 				zend_cannot_add_element();
3747 				return 0;
3748 			}
3749 			zval_add_ref(zv);
3750 		}
3751 	} ZEND_HASH_FOREACH_END();
3752 	return 1;
3753 }
3754 /* }}} */
3755 
3756 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3757 {
3758 	zval *src_entry;
3759 	zend_string *string_key;
3760 
3761 	if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
3762 		zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
3763 		ZEND_HASH_FILL_PACKED(dest) {
3764 			ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3765 				if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
3766 					UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
3767 					src_entry = Z_REFVAL_P(src_entry);
3768 				}
3769 				Z_TRY_ADDREF_P(src_entry);
3770 				ZEND_HASH_FILL_ADD(src_entry);
3771 			} ZEND_HASH_FOREACH_END();
3772 		} ZEND_HASH_FILL_END();
3773 	} else {
3774 		ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3775 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3776 				Z_REFCOUNT_P(src_entry) == 1)) {
3777 				src_entry = Z_REFVAL_P(src_entry);
3778 			}
3779 			Z_TRY_ADDREF_P(src_entry);
3780 			if (string_key) {
3781 				zend_hash_update(dest, string_key, src_entry);
3782 			} else {
3783 				zend_hash_next_index_insert_new(dest, src_entry);
3784 			}
3785 		} ZEND_HASH_FOREACH_END();
3786 	}
3787 	return 1;
3788 }
3789 /* }}} */
3790 
3791 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
3792 {
3793 	zval *src_entry, *dest_entry, *src_zval, *dest_zval;
3794 	zend_string *string_key;
3795 	zend_ulong num_key;
3796 	int ret;
3797 
3798 	ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
3799 		src_zval = src_entry;
3800 		ZVAL_DEREF(src_zval);
3801 		if (string_key) {
3802 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3803 				(dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
3804 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
3805 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3806 
3807 				zval *zv = zend_hash_update(dest, string_key, src_entry);
3808 				zval_add_ref(zv);
3809 				continue;
3810 			}
3811 		} else {
3812 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3813 				(dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
3814 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
3815 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3816 
3817 				zval *zv = zend_hash_index_update(dest, num_key, src_entry);
3818 				zval_add_ref(zv);
3819 				continue;
3820 			}
3821 		}
3822 
3823 		dest_zval = dest_entry;
3824 		ZVAL_DEREF(dest_zval);
3825 		if (Z_IS_RECURSIVE_P(dest_zval) ||
3826 		    Z_IS_RECURSIVE_P(src_zval) ||
3827 		    (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))) {
3828 			zend_throw_error(NULL, "Recursion detected");
3829 			return 0;
3830 		}
3831 
3832 		ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
3833 		SEPARATE_ZVAL(dest_entry);
3834 		dest_zval = dest_entry;
3835 
3836 		if (Z_REFCOUNTED_P(dest_zval)) {
3837 			Z_PROTECT_RECURSION_P(dest_zval);
3838 		}
3839 		if (Z_REFCOUNTED_P(src_zval)) {
3840 			Z_PROTECT_RECURSION_P(src_zval);
3841 		}
3842 
3843 		ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3844 
3845 		if (Z_REFCOUNTED_P(dest_zval)) {
3846 			Z_UNPROTECT_RECURSION_P(dest_zval);
3847 		}
3848 		if (Z_REFCOUNTED_P(src_zval)) {
3849 			Z_UNPROTECT_RECURSION_P(src_zval);
3850 		}
3851 
3852 		if (!ret) {
3853 			return 0;
3854 		}
3855 	} ZEND_HASH_FOREACH_END();
3856 
3857 	return 1;
3858 }
3859 /* }}} */
3860 
3861 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
3862 {
3863 	zval *args = NULL;
3864 	zval *arg;
3865 	int argc, i;
3866 	HashTable *dest;
3867 
3868 	ZEND_PARSE_PARAMETERS_START(1, -1)
3869 		Z_PARAM_VARIADIC('+', args, argc)
3870 	ZEND_PARSE_PARAMETERS_END();
3871 
3872 
3873 	for (i = 0; i < argc; i++) {
3874 		zval *arg = args + i;
3875 
3876 		if (Z_TYPE_P(arg) != IS_ARRAY) {
3877 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
3878 			RETURN_THROWS();
3879 		}
3880 	}
3881 
3882 	/* copy first array */
3883 	arg = args;
3884 	dest = zend_array_dup(Z_ARRVAL_P(arg));
3885 	ZVAL_ARR(return_value, dest);
3886 	if (recursive) {
3887 		for (i = 1; i < argc; i++) {
3888 			arg = args + i;
3889 			php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
3890 		}
3891 	} else {
3892 		for (i = 1; i < argc; i++) {
3893 			arg = args + i;
3894 			zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
3895 		}
3896 	}
3897 }
3898 /* }}} */
3899 
3900 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
3901 {
3902 	zval *args = NULL;
3903 	zval *arg;
3904 	int argc, i;
3905 	zval *src_entry;
3906 	HashTable *src, *dest;
3907 	uint32_t count = 0;
3908 
3909 	ZEND_PARSE_PARAMETERS_START(0, -1)
3910 		Z_PARAM_VARIADIC('+', args, argc)
3911 	ZEND_PARSE_PARAMETERS_END();
3912 
3913 	if (argc == 0) {
3914 		RETURN_EMPTY_ARRAY();
3915 	}
3916 
3917 	for (i = 0; i < argc; i++) {
3918 		zval *arg = args + i;
3919 
3920 		if (Z_TYPE_P(arg) != IS_ARRAY) {
3921 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
3922 			RETURN_THROWS();
3923 		}
3924 		count += zend_hash_num_elements(Z_ARRVAL_P(arg));
3925 	}
3926 
3927 	if (argc == 2) {
3928 		zval *ret = NULL;
3929 
3930 		if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
3931 			ret = &args[1];
3932 		} else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
3933 			ret = &args[0];
3934 		}
3935 		if (ret) {
3936 			if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
3937 				if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
3938 					ZVAL_COPY(return_value, ret);
3939 					return;
3940 				}
3941 			} else {
3942 				bool copy = 1;
3943 				zend_string *string_key;
3944 
3945 				ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
3946 					if (!string_key) {
3947 						copy = 0;
3948 						break;
3949 					}
3950 				} ZEND_HASH_FOREACH_END();
3951 				if (copy) {
3952 					ZVAL_COPY(return_value, ret);
3953 					return;
3954 				}
3955 			}
3956 		}
3957 	}
3958 
3959 	arg = args;
3960 	src  = Z_ARRVAL_P(arg);
3961 	/* copy first array */
3962 	array_init_size(return_value, count);
3963 	dest = Z_ARRVAL_P(return_value);
3964 	if (HT_IS_PACKED(src)) {
3965 		zend_hash_real_init_packed(dest);
3966 		ZEND_HASH_FILL_PACKED(dest) {
3967 			ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3968 				if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3969 					Z_REFCOUNT_P(src_entry) == 1)) {
3970 					src_entry = Z_REFVAL_P(src_entry);
3971 				}
3972 				Z_TRY_ADDREF_P(src_entry);
3973 				ZEND_HASH_FILL_ADD(src_entry);
3974 			} ZEND_HASH_FOREACH_END();
3975 		} ZEND_HASH_FILL_END();
3976 	} else {
3977 		zend_string *string_key;
3978 		zend_hash_real_init_mixed(dest);
3979 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3980 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3981 				Z_REFCOUNT_P(src_entry) == 1)) {
3982 				src_entry = Z_REFVAL_P(src_entry);
3983 			}
3984 			Z_TRY_ADDREF_P(src_entry);
3985 			if (EXPECTED(string_key)) {
3986 				_zend_hash_append(dest, string_key, src_entry);
3987 			} else {
3988 				zend_hash_next_index_insert_new(dest, src_entry);
3989 			}
3990 		} ZEND_HASH_FOREACH_END();
3991 	}
3992 	if (recursive) {
3993 		for (i = 1; i < argc; i++) {
3994 			arg = args + i;
3995 			php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
3996 		}
3997 	} else {
3998 		for (i = 1; i < argc; i++) {
3999 			arg = args + i;
4000 			php_array_merge(dest, Z_ARRVAL_P(arg));
4001 		}
4002 	}
4003 }
4004 /* }}} */
4005 
4006 /* {{{ Merges elements from passed arrays into one array */
4007 PHP_FUNCTION(array_merge)
4008 {
4009 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4010 }
4011 /* }}} */
4012 
4013 /* {{{ Recursively merges elements from passed arrays into one array */
4014 PHP_FUNCTION(array_merge_recursive)
4015 {
4016 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4017 }
4018 /* }}} */
4019 
4020 /* {{{ Replaces elements from passed arrays into one array */
4021 PHP_FUNCTION(array_replace)
4022 {
4023 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4024 }
4025 /* }}} */
4026 
4027 /* {{{ Recursively replaces elements from passed arrays into one array */
4028 PHP_FUNCTION(array_replace_recursive)
4029 {
4030 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4031 }
4032 /* }}} */
4033 
4034 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4035 PHP_FUNCTION(array_keys)
4036 {
4037 	zval *input,				/* Input array */
4038 	     *search_value = NULL,	/* Value to search for */
4039 	     *entry,				/* An entry in the input array */
4040 	       new_val;				/* New value */
4041 	bool strict = 0;		/* do strict comparison */
4042 	zend_ulong num_idx;
4043 	zend_string *str_idx;
4044 	zend_array *arrval;
4045 	zend_ulong elem_count;
4046 
4047 	ZEND_PARSE_PARAMETERS_START(1, 3)
4048 		Z_PARAM_ARRAY(input)
4049 		Z_PARAM_OPTIONAL
4050 		Z_PARAM_ZVAL(search_value)
4051 		Z_PARAM_BOOL(strict)
4052 	ZEND_PARSE_PARAMETERS_END();
4053 	arrval = Z_ARRVAL_P(input);
4054 	elem_count = zend_hash_num_elements(arrval);
4055 
4056 	/* Base case: empty input */
4057 	if (!elem_count) {
4058 		RETURN_COPY(input);
4059 	}
4060 
4061 	/* Initialize return array */
4062 	if (search_value != NULL) {
4063 		array_init(return_value);
4064 
4065 		if (strict) {
4066 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4067 				ZVAL_DEREF(entry);
4068 				if (fast_is_identical_function(search_value, entry)) {
4069 					if (str_idx) {
4070 						ZVAL_STR_COPY(&new_val, str_idx);
4071 					} else {
4072 						ZVAL_LONG(&new_val, num_idx);
4073 					}
4074 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4075 				}
4076 			} ZEND_HASH_FOREACH_END();
4077 		} else {
4078 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4079 				if (fast_equal_check_function(search_value, entry)) {
4080 					if (str_idx) {
4081 						ZVAL_STR_COPY(&new_val, str_idx);
4082 					} else {
4083 						ZVAL_LONG(&new_val, num_idx);
4084 					}
4085 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4086 				}
4087 			} ZEND_HASH_FOREACH_END();
4088 		}
4089 	} else {
4090 		array_init_size(return_value, elem_count);
4091 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4092 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4093 			if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4094 				/* Optimistic case: range(0..n-1) for vector-like packed array */
4095 				zend_ulong lval = 0;
4096 
4097 				for (; lval < elem_count; ++lval) {
4098 					ZEND_HASH_FILL_SET_LONG(lval);
4099 					ZEND_HASH_FILL_NEXT();
4100 				}
4101 			} else {
4102 				/* Go through input array and add keys to the return array */
4103 				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4104 					if (str_idx) {
4105 						ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4106 					} else {
4107 						ZEND_HASH_FILL_SET_LONG(num_idx);
4108 					}
4109 					ZEND_HASH_FILL_NEXT();
4110 				} ZEND_HASH_FOREACH_END();
4111 			}
4112 		} ZEND_HASH_FILL_END();
4113 	}
4114 }
4115 /* }}} */
4116 
4117 /* {{{ Get the key of the first element of the array */
4118 PHP_FUNCTION(array_key_first)
4119 {
4120 	zval *stack;    /* Input stack */
4121 
4122 	ZEND_PARSE_PARAMETERS_START(1, 1)
4123 		Z_PARAM_ARRAY(stack)
4124 	ZEND_PARSE_PARAMETERS_END();
4125 
4126 	HashTable *target_hash = Z_ARRVAL_P (stack);
4127 	HashPosition pos = 0;
4128 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4129 }
4130 /* }}} */
4131 
4132 /* {{{ Get the key of the last element of the array */
4133 PHP_FUNCTION(array_key_last)
4134 {
4135 	zval *stack;    /* Input stack */
4136 	HashPosition pos;
4137 
4138 	ZEND_PARSE_PARAMETERS_START(1, 1)
4139 		Z_PARAM_ARRAY(stack)
4140 	ZEND_PARSE_PARAMETERS_END();
4141 
4142 	HashTable *target_hash = Z_ARRVAL_P (stack);
4143 	zend_hash_internal_pointer_end_ex(target_hash, &pos);
4144 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4145 }
4146 /* }}} */
4147 
4148 /* {{{ Return just the values from the input array */
4149 PHP_FUNCTION(array_values)
4150 {
4151 	zval	 *input;		/* Input array */
4152 	zend_array *arrval;
4153 	zend_long arrlen;
4154 
4155 	ZEND_PARSE_PARAMETERS_START(1, 1)
4156 		Z_PARAM_ARRAY(input)
4157 	ZEND_PARSE_PARAMETERS_END();
4158 
4159 	arrval = Z_ARRVAL_P(input);
4160 
4161 	/* Return empty input as is */
4162 	arrlen = zend_hash_num_elements(arrval);
4163 	if (!arrlen) {
4164 		RETURN_EMPTY_ARRAY();
4165 	}
4166 
4167 	/* Return vector-like packed arrays as-is */
4168 	if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4169 		arrval->nNextFreeElement == arrlen) {
4170 		RETURN_COPY(input);
4171 	}
4172 
4173 	RETURN_ARR(zend_array_to_list(arrval));
4174 }
4175 /* }}} */
4176 
4177 /* {{{ Return the value as key and the frequency of that value in input as value */
4178 PHP_FUNCTION(array_count_values)
4179 {
4180 	zval	*input,		/* Input array */
4181 			*entry,		/* An entry in the input array */
4182 			*tmp;
4183 	HashTable *myht;
4184 
4185 	ZEND_PARSE_PARAMETERS_START(1, 1)
4186 		Z_PARAM_ARRAY(input)
4187 	ZEND_PARSE_PARAMETERS_END();
4188 
4189 	/* Initialize return array */
4190 	array_init(return_value);
4191 
4192 	/* Go through input array and add values to the return array */
4193 	myht = Z_ARRVAL_P(input);
4194 	ZEND_HASH_FOREACH_VAL(myht, entry) {
4195 		ZVAL_DEREF(entry);
4196 		if (Z_TYPE_P(entry) == IS_LONG) {
4197 			if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4198 				zval data;
4199 				ZVAL_LONG(&data, 1);
4200 				zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4201 			} else {
4202 				Z_LVAL_P(tmp)++;
4203 			}
4204 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4205 			if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4206 				zval data;
4207 				ZVAL_LONG(&data, 1);
4208 				zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4209 			} else {
4210 				Z_LVAL_P(tmp)++;
4211 			}
4212 		} else {
4213 			php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4214 		}
4215 	} ZEND_HASH_FOREACH_END();
4216 }
4217 /* }}} */
4218 
4219 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4220 {
4221 	zval *prop = NULL;
4222 
4223 	if (Z_TYPE_P(data) == IS_OBJECT) {
4224 		zend_string *tmp_str;
4225 		/* If name is an integer convert integer to string */
4226 		if (name_str == NULL) {
4227 			tmp_str = zend_long_to_str(name_long);
4228 		} else {
4229 			tmp_str = zend_string_copy(name_str);
4230 		}
4231 		/* The has_property check is first performed in "exists" mode (which returns true for
4232 		 * properties that are null but exist) and then in "has" mode to handle objects that
4233 		 * implement __isset (which is not called in "exists" mode). */
4234 		if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4235 				|| Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4236 			prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4237 			if (prop) {
4238 				ZVAL_DEREF(prop);
4239 				if (prop != rv) {
4240 					Z_TRY_ADDREF_P(prop);
4241 				}
4242 			}
4243 		}
4244 		zend_string_release(tmp_str);
4245 	} else if (Z_TYPE_P(data) == IS_ARRAY) {
4246 		/* Name is a string */
4247 		if (name_str != NULL) {
4248 			prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4249 		} else {
4250 			prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4251 		}
4252 		if (prop) {
4253 			ZVAL_DEREF(prop);
4254 			Z_TRY_ADDREF_P(prop);
4255 		}
4256 	}
4257 
4258 	return prop;
4259 }
4260 /* }}} */
4261 
4262 /* {{{ Return the values from a single column in the input array, identified by the
4263    value_key and optionally indexed by the index_key */
4264 PHP_FUNCTION(array_column)
4265 {
4266 	HashTable *input;
4267 	zval *colval, *data, rv;
4268 	zend_string *column_str = NULL;
4269 	zend_long column_long = 0;
4270 	bool column_is_null = 0;
4271 	zend_string *index_str = NULL;
4272 	zend_long index_long = 0;
4273 	bool index_is_null = 1;
4274 
4275 	ZEND_PARSE_PARAMETERS_START(2, 3)
4276 		Z_PARAM_ARRAY_HT(input)
4277 		Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4278 		Z_PARAM_OPTIONAL
4279 		Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4280 	ZEND_PARSE_PARAMETERS_END();
4281 
4282 	void* cache_slot_column[3] = { NULL, NULL, NULL };
4283 	void* cache_slot_index[3] = { NULL, NULL, NULL };
4284 
4285 	array_init_size(return_value, zend_hash_num_elements(input));
4286 	/* Index param is not passed */
4287 	if (index_is_null) {
4288 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4289 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4290 			ZEND_HASH_FOREACH_VAL(input, data) {
4291 				ZVAL_DEREF(data);
4292 				if (column_is_null) {
4293 					Z_TRY_ADDREF_P(data);
4294 					colval = data;
4295 				} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4296 					continue;
4297 				}
4298 				ZEND_HASH_FILL_ADD(colval);
4299 			} ZEND_HASH_FOREACH_END();
4300 		} ZEND_HASH_FILL_END();
4301 	} else {
4302 		ZEND_HASH_FOREACH_VAL(input, data) {
4303 			ZVAL_DEREF(data);
4304 
4305 			if (column_is_null) {
4306 				Z_TRY_ADDREF_P(data);
4307 				colval = data;
4308 			} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4309 				continue;
4310 			}
4311 
4312 			zval rv;
4313 			zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4314 			if (keyval) {
4315 				array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4316 				zval_ptr_dtor(colval);
4317 				zval_ptr_dtor(keyval);
4318 			} else {
4319 				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4320 			}
4321 		} ZEND_HASH_FOREACH_END();
4322 	}
4323 }
4324 /* }}} */
4325 
4326 /* {{{ Return input as a new array with the order of the entries reversed */
4327 PHP_FUNCTION(array_reverse)
4328 {
4329 	zval	 *input,				/* Input array */
4330 			 *entry;				/* An entry in the input array */
4331 	zend_string *string_key;
4332 	zend_ulong	  num_key;
4333 	bool preserve_keys = 0;	/* whether to preserve keys */
4334 
4335 	ZEND_PARSE_PARAMETERS_START(1, 2)
4336 		Z_PARAM_ARRAY(input)
4337 		Z_PARAM_OPTIONAL
4338 		Z_PARAM_BOOL(preserve_keys)
4339 	ZEND_PARSE_PARAMETERS_END();
4340 
4341 	/* Initialize return array */
4342 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4343 	if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4344 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4345 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4346 			ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4347 				if (UNEXPECTED(Z_ISREF_P(entry) &&
4348 					Z_REFCOUNT_P(entry) == 1)) {
4349 					entry = Z_REFVAL_P(entry);
4350 				}
4351 				Z_TRY_ADDREF_P(entry);
4352 				ZEND_HASH_FILL_ADD(entry);
4353 			} ZEND_HASH_FOREACH_END();
4354 		} ZEND_HASH_FILL_END();
4355 	} else {
4356 		ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4357 			if (string_key) {
4358 				entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4359 			} else {
4360 				if (preserve_keys) {
4361 					entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4362 				} else {
4363 					entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4364 				}
4365 			}
4366 			zval_add_ref(entry);
4367 		} ZEND_HASH_FOREACH_END();
4368 	}
4369 }
4370 /* }}} */
4371 
4372 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4373 PHP_FUNCTION(array_pad)
4374 {
4375 	zval  *input;		/* Input array */
4376 	zval  *pad_value;	/* Padding value obviously */
4377 	zend_long pad_size;		/* Size to pad to */
4378 	zend_long pad_size_abs;	/* Absolute value of pad_size */
4379 	zend_long input_size;		/* Size of the input array */
4380 	zend_long num_pads;		/* How many pads do we need */
4381 	zend_long i;
4382 	zend_string *key;
4383 	zval *value;
4384 
4385 	ZEND_PARSE_PARAMETERS_START(3, 3)
4386 		Z_PARAM_ARRAY(input)
4387 		Z_PARAM_LONG(pad_size)
4388 		Z_PARAM_ZVAL(pad_value)
4389 	ZEND_PARSE_PARAMETERS_END();
4390 
4391 	/* Do some initial calculations */
4392 	input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4393 	pad_size_abs = ZEND_ABS(pad_size);
4394 	if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) {
4395 		zend_argument_value_error(2, "must be less than or equal to 1048576");
4396 		RETURN_THROWS();
4397 	}
4398 
4399 	if (input_size >= pad_size_abs) {
4400 		/* Copy the original array */
4401 		ZVAL_COPY(return_value, input);
4402 		return;
4403 	}
4404 
4405 	num_pads = pad_size_abs - input_size;
4406 	if (Z_REFCOUNTED_P(pad_value)) {
4407 		GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4408 	}
4409 
4410 	array_init_size(return_value, pad_size_abs);
4411 	if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4412 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4413 
4414 		if (pad_size < 0) {
4415 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4416 				for (i = 0; i < num_pads; i++) {
4417 					ZEND_HASH_FILL_ADD(pad_value);
4418 				}
4419 			} ZEND_HASH_FILL_END();
4420 		}
4421 
4422 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4423 			ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4424 				Z_TRY_ADDREF_P(value);
4425 				ZEND_HASH_FILL_ADD(value);
4426 			} ZEND_HASH_FOREACH_END();
4427 		} ZEND_HASH_FILL_END();
4428 
4429 		if (pad_size > 0) {
4430 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4431 				for (i = 0; i < num_pads; i++) {
4432 					ZEND_HASH_FILL_ADD(pad_value);
4433 				}
4434 			} ZEND_HASH_FILL_END();
4435 		}
4436 	} else {
4437 		if (pad_size < 0) {
4438 			for (i = 0; i < num_pads; i++) {
4439 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4440 			}
4441 		}
4442 
4443 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4444 			Z_TRY_ADDREF_P(value);
4445 			if (key) {
4446 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4447 			} else {
4448 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4449 			}
4450 		} ZEND_HASH_FOREACH_END();
4451 
4452 		if (pad_size > 0) {
4453 			for (i = 0; i < num_pads; i++) {
4454 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4455 			}
4456 		}
4457 	}
4458 }
4459 /* }}} */
4460 
4461 /* {{{ Return array with key <-> value flipped */
4462 PHP_FUNCTION(array_flip)
4463 {
4464 	zval *array, *entry, data;
4465 	zend_ulong num_idx;
4466 	zend_string *str_idx;
4467 
4468 	ZEND_PARSE_PARAMETERS_START(1, 1)
4469 		Z_PARAM_ARRAY(array)
4470 	ZEND_PARSE_PARAMETERS_END();
4471 
4472 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4473 
4474 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4475 		ZVAL_DEREF(entry);
4476 		if (Z_TYPE_P(entry) == IS_LONG) {
4477 			if (str_idx) {
4478 				ZVAL_STR_COPY(&data, str_idx);
4479 			} else {
4480 				ZVAL_LONG(&data, num_idx);
4481 			}
4482 			zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4483 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4484 			if (str_idx) {
4485 				ZVAL_STR_COPY(&data, str_idx);
4486 			} else {
4487 				ZVAL_LONG(&data, num_idx);
4488 			}
4489 			zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4490 		} else {
4491 			php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4492 		}
4493 	} ZEND_HASH_FOREACH_END();
4494 }
4495 /* }}} */
4496 
4497 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4498 PHP_FUNCTION(array_change_key_case)
4499 {
4500 	zval *array, *entry;
4501 	zend_string *string_key;
4502 	zend_string *new_key;
4503 	zend_ulong num_key;
4504 	zend_long change_to_upper=0;
4505 
4506 	ZEND_PARSE_PARAMETERS_START(1, 2)
4507 		Z_PARAM_ARRAY(array)
4508 		Z_PARAM_OPTIONAL
4509 		Z_PARAM_LONG(change_to_upper)
4510 	ZEND_PARSE_PARAMETERS_END();
4511 
4512 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4513 
4514 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4515 		if (!string_key) {
4516 			entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4517 		} else {
4518 			if (change_to_upper) {
4519 				new_key = zend_string_toupper(string_key);
4520 			} else {
4521 				new_key = zend_string_tolower(string_key);
4522 			}
4523 			entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4524 			zend_string_release_ex(new_key, 0);
4525 		}
4526 
4527 		zval_add_ref(entry);
4528 	} ZEND_HASH_FOREACH_END();
4529 }
4530 /* }}} */
4531 
4532 struct bucketindex {
4533 	Bucket b;
4534 	unsigned int i;
4535 };
4536 
4537 static void array_bucketindex_swap(void *p, void *q)
4538 {
4539 	struct bucketindex *f = (struct bucketindex *)p;
4540 	struct bucketindex *g = (struct bucketindex *)q;
4541 	struct bucketindex t;
4542 	t = *f;
4543 	*f = *g;
4544 	*g = t;
4545 }
4546 
4547 /* {{{ Removes duplicate values from array */
4548 PHP_FUNCTION(array_unique)
4549 {
4550 	zval *array;
4551 	Bucket *p;
4552 	zend_long sort_type = PHP_SORT_STRING;
4553 	bucket_compare_func_t cmp;
4554 	struct bucketindex *arTmp, *cmpdata, *lastkept;
4555 	uint32_t i, idx;
4556 
4557 	ZEND_PARSE_PARAMETERS_START(1, 2)
4558 		Z_PARAM_ARRAY(array)
4559 		Z_PARAM_OPTIONAL
4560 		Z_PARAM_LONG(sort_type)
4561 	ZEND_PARSE_PARAMETERS_END();
4562 
4563 	if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {	/* nothing to do */
4564 		ZVAL_COPY(return_value, array);
4565 		return;
4566 	}
4567 
4568 	if (sort_type == PHP_SORT_STRING) {
4569 		HashTable seen;
4570 		zend_long num_key;
4571 		zend_string *str_key;
4572 		zval *val;
4573 
4574 		zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4575 		array_init(return_value);
4576 
4577 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4578 			zval *retval;
4579 			if (Z_TYPE_P(val) == IS_STRING) {
4580 				retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4581 			} else {
4582 				zend_string *tmp_str_val;
4583 				zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4584 				retval = zend_hash_add_empty_element(&seen, str_val);
4585 				zend_tmp_string_release(tmp_str_val);
4586 			}
4587 
4588 			if (retval) {
4589 				/* First occurrence of the value */
4590 				if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4591 					ZVAL_DEREF(val);
4592 				}
4593 				Z_TRY_ADDREF_P(val);
4594 
4595 				if (str_key) {
4596 					zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4597 				} else {
4598 					zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4599 				}
4600 			}
4601 		} ZEND_HASH_FOREACH_END();
4602 
4603 		zend_hash_destroy(&seen);
4604 		return;
4605 	}
4606 
4607 	cmp = php_get_data_compare_func_unstable(sort_type, 0);
4608 
4609 	RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4610 
4611 	/* create and sort array with pointers to the target_hash buckets */
4612 	arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4613 	if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4614 		zval *zv = Z_ARRVAL_P(array)->arPacked;
4615 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4616 			if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4617 			ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4618 			arTmp[i].b.h = idx;
4619 			arTmp[i].b.key = NULL;
4620 			arTmp[i].i = i;
4621 			i++;
4622 		}
4623 	} else {
4624 		p = Z_ARRVAL_P(array)->arData;
4625 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4626 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
4627 			arTmp[i].b = *p;
4628 			arTmp[i].i = i;
4629 			i++;
4630 		}
4631 	}
4632 	ZVAL_UNDEF(&arTmp[i].b.val);
4633 	zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4634 			(compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4635 	/* go through the sorted array and delete duplicates from the copy */
4636 	lastkept = arTmp;
4637 	for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4638 		if (cmp(&lastkept->b, &cmpdata->b)) {
4639 			lastkept = cmpdata;
4640 		} else {
4641 			if (lastkept->i > cmpdata->i) {
4642 				p = &lastkept->b;
4643 				lastkept = cmpdata;
4644 			} else {
4645 				p = &cmpdata->b;
4646 			}
4647 			if (p->key == NULL) {
4648 				zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4649 			} else {
4650 				zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4651 			}
4652 		}
4653 	}
4654 	pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4655 }
4656 /* }}} */
4657 
4658 static int zval_compare(zval *first, zval *second) /* {{{ */
4659 {
4660 	return string_compare_function(first, second);
4661 }
4662 /* }}} */
4663 
4664 static int zval_user_compare(zval *a, zval *b) /* {{{ */
4665 {
4666 	zval args[2];
4667 	zval retval;
4668 
4669 	ZVAL_COPY_VALUE(&args[0], a);
4670 	ZVAL_COPY_VALUE(&args[1], b);
4671 
4672 	BG(user_compare_fci).param_count = 2;
4673 	BG(user_compare_fci).params = args;
4674 	BG(user_compare_fci).retval = &retval;
4675 
4676 	if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4677 		zend_long ret = zval_get_long(&retval);
4678 		zval_ptr_dtor(&retval);
4679 		return ZEND_NORMALIZE_BOOL(ret);
4680 	} else {
4681 		return 0;
4682 	}
4683 }
4684 /* }}} */
4685 
4686 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4687 {
4688 	int argc, i;
4689 	zval *args;
4690 	int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4691 	bool ok;
4692 	zval *val, *data;
4693 	char *param_spec;
4694 	zend_string *key;
4695 	zend_ulong h;
4696 
4697 	/* Get the argument count */
4698 	argc = ZEND_NUM_ARGS();
4699 	if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4700 		/* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
4701 		param_spec = "+f";
4702 		intersect_data_compare_func = zval_user_compare;
4703 	} else {
4704 		/* 	INTERSECT_COMP_DATA_NONE - array_intersect_key()
4705 			INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
4706 		param_spec = "+";
4707 
4708 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4709 			intersect_data_compare_func = zval_compare;
4710 		}
4711 	}
4712 
4713 	if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4714 		RETURN_THROWS();
4715 	}
4716 
4717 	for (i = 0; i < argc; i++) {
4718 		if (Z_TYPE(args[i]) != IS_ARRAY) {
4719 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
4720 			RETURN_THROWS();
4721 		}
4722 	}
4723 
4724 	array_init(return_value);
4725 
4726 	/* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
4727 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4728 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4729 			val = Z_REFVAL_P(val);
4730 		}
4731 		if (key == NULL) {
4732 			ok = 1;
4733 			for (i = 1; i < argc; i++) {
4734 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
4735 					(intersect_data_compare_func &&
4736 					intersect_data_compare_func(val, data) != 0)
4737 				) {
4738 					ok = 0;
4739 					break;
4740 				}
4741 			}
4742 			if (ok) {
4743 				Z_TRY_ADDREF_P(val);
4744 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4745 			}
4746 		} else {
4747 			ok = 1;
4748 			for (i = 1; i < argc; i++) {
4749 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
4750 					(intersect_data_compare_func &&
4751 					intersect_data_compare_func(val, data) != 0)
4752 				) {
4753 					ok = 0;
4754 					break;
4755 				}
4756 			}
4757 			if (ok) {
4758 				Z_TRY_ADDREF_P(val);
4759 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
4760 			}
4761 		}
4762 	} ZEND_HASH_FOREACH_END();
4763 }
4764 /* }}} */
4765 
4766 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
4767 {
4768 	zval *args = NULL;
4769 	HashTable *hash;
4770 	int arr_argc, i, c = 0;
4771 	uint32_t idx;
4772 	Bucket **lists, *list, **ptrs, *p;
4773 	char *param_spec;
4774 	zend_fcall_info fci1, fci2;
4775 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
4776 	zend_fcall_info *fci_key = NULL, *fci_data;
4777 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
4778 	PHP_ARRAY_CMP_FUNC_VARS;
4779 
4780 	bucket_compare_func_t intersect_key_compare_func;
4781 	bucket_compare_func_t intersect_data_compare_func;
4782 
4783 	if (behavior == INTERSECT_NORMAL) {
4784 		intersect_key_compare_func = php_array_key_compare_string;
4785 
4786 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4787 			/* array_intersect() */
4788 			param_spec = "+";
4789 			intersect_data_compare_func = php_array_data_compare_string_unstable;
4790 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4791 			/* array_uintersect() */
4792 			param_spec = "+f";
4793 			intersect_data_compare_func = php_array_user_compare_unstable;
4794 		} else {
4795 			ZEND_ASSERT(0 && "Invalid data_compare_type");
4796 			return;
4797 		}
4798 
4799 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
4800 			RETURN_THROWS();
4801 		}
4802 		fci_data = &fci1;
4803 		fci_data_cache = &fci1_cache;
4804 
4805 	} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4806 		/* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
4807 		 * no comparison of the data is done (part of INTERSECT_ASSOC) */
4808 
4809 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
4810 			/* array_intersect_assoc() or array_intersect_key() */
4811 			param_spec = "+";
4812 			intersect_key_compare_func = php_array_key_compare_string_unstable;
4813 			intersect_data_compare_func = php_array_data_compare_string_unstable;
4814 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
4815 			/* array_uintersect_assoc() */
4816 			param_spec = "+f";
4817 			intersect_key_compare_func = php_array_key_compare_string_unstable;
4818 			intersect_data_compare_func = php_array_user_compare_unstable;
4819 			fci_data = &fci1;
4820 			fci_data_cache = &fci1_cache;
4821 		} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
4822 			/* array_intersect_uassoc() or array_intersect_ukey() */
4823 			param_spec = "+f";
4824 			intersect_key_compare_func = php_array_user_key_compare_unstable;
4825 			intersect_data_compare_func = php_array_data_compare_string_unstable;
4826 			fci_key = &fci1;
4827 			fci_key_cache = &fci1_cache;
4828 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
4829 			/* array_uintersect_uassoc() */
4830 			param_spec = "+ff";
4831 			intersect_key_compare_func = php_array_user_key_compare_unstable;
4832 			intersect_data_compare_func = php_array_user_compare_unstable;
4833 			fci_data = &fci1;
4834 			fci_data_cache = &fci1_cache;
4835 			fci_key = &fci2;
4836 			fci_key_cache = &fci2_cache;
4837 		} else {
4838 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
4839 			return;
4840 		}
4841 
4842 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
4843 			RETURN_THROWS();
4844 		}
4845 
4846 	} else {
4847 		ZEND_ASSERT(0 && "Invalid behavior");
4848 		return;
4849 	}
4850 
4851 	PHP_ARRAY_CMP_FUNC_BACKUP();
4852 
4853 	/* for each argument, create and sort list with pointers to the hash buckets */
4854 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4855 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4856 
4857 	if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
4858 		BG(user_compare_fci) = *fci_data;
4859 		BG(user_compare_fci_cache) = *fci_data_cache;
4860 	} else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
4861 		BG(user_compare_fci) = *fci_key;
4862 		BG(user_compare_fci_cache) = *fci_key_cache;
4863 	}
4864 
4865 	for (i = 0; i < arr_argc; i++) {
4866 		if (Z_TYPE(args[i]) != IS_ARRAY) {
4867 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
4868 			arr_argc = i; /* only free up to i - 1 */
4869 			goto out;
4870 		}
4871 		hash = Z_ARRVAL(args[i]);
4872 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
4873 		lists[i] = list;
4874 		ptrs[i] = list;
4875 		if (HT_IS_PACKED(hash)) {
4876 			zval *zv = hash->arPacked;
4877 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
4878 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4879 				ZVAL_COPY_VALUE(&list->val, zv);
4880 				list->h = idx;
4881 				list->key = NULL;
4882 				list++;
4883 			}
4884 		} else {
4885 			p = hash->arData;
4886 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
4887 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
4888 				*list++ = *p;
4889 			}
4890 		}
4891 		ZVAL_UNDEF(&list->val);
4892 		if (hash->nNumOfElements > 1) {
4893 			if (behavior == INTERSECT_NORMAL) {
4894 				zend_sort((void *) lists[i], hash->nNumOfElements,
4895 						sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
4896 						(swap_func_t)zend_hash_bucket_swap);
4897 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4898 				zend_sort((void *) lists[i], hash->nNumOfElements,
4899 						sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
4900 						(swap_func_t)zend_hash_bucket_swap);
4901 			}
4902 		}
4903 	}
4904 
4905 	/* copy the argument array */
4906 	RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
4907 
4908 	/* go through the lists and look for common values */
4909 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
4910 		if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
4911 			&& key_compare_type == INTERSECT_COMP_KEY_USER) {
4912 			BG(user_compare_fci) = *fci_key;
4913 			BG(user_compare_fci_cache) = *fci_key_cache;
4914 		}
4915 
4916 		for (i = 1; i < arr_argc; i++) {
4917 			if (behavior & INTERSECT_NORMAL) {
4918 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
4919 					ptrs[i]++;
4920 				}
4921 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4922 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
4923 					ptrs[i]++;
4924 				}
4925 				if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
4926 					/* this means that ptrs[i] is not NULL so we can compare
4927 					 * and "c==0" is from last operation
4928 					 * in this branch of code we enter only when INTERSECT_ASSOC
4929 					 * since when we have INTERSECT_KEY compare of data is not wanted. */
4930 					if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4931 						BG(user_compare_fci) = *fci_data;
4932 						BG(user_compare_fci_cache) = *fci_data_cache;
4933 					}
4934 					if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
4935 						c = 1;
4936 						if (key_compare_type == INTERSECT_COMP_KEY_USER) {
4937 							BG(user_compare_fci) = *fci_key;
4938 							BG(user_compare_fci_cache) = *fci_key_cache;
4939 							/* When KEY_USER, the last parameter is always the callback */
4940 						}
4941 						/* we are going to the break */
4942 					} else {
4943 						/* continue looping */
4944 					}
4945 				}
4946 			}
4947 			if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
4948 				/* delete any values corresponding to remains of ptrs[0] */
4949 				/* and exit because they do not present in at least one of */
4950 				/* the other arguments */
4951 				for (;;) {
4952 					p = ptrs[0]++;
4953 					if (Z_TYPE(p->val) == IS_UNDEF) {
4954 						goto out;
4955 					}
4956 					if (p->key == NULL) {
4957 						zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4958 					} else {
4959 						zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4960 					}
4961 				}
4962 			}
4963 			if (c) /* here we get if not all are equal */
4964 				break;
4965 			ptrs[i]++;
4966 		}
4967 		if (c) {
4968 			/* Value of ptrs[0] not in all arguments, delete all entries */
4969 			/* with value < value of ptrs[i] */
4970 			for (;;) {
4971 				p = ptrs[0];
4972 				if (p->key == NULL) {
4973 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4974 				} else {
4975 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4976 				}
4977 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4978 					goto out;
4979 				}
4980 				if (behavior == INTERSECT_NORMAL) {
4981 					if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
4982 						break;
4983 					}
4984 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4985 					/* no need of looping because indexes are unique */
4986 					break;
4987 				}
4988 			}
4989 		} else {
4990 			/* ptrs[0] is present in all the arguments */
4991 			/* Skip all entries with same value as ptrs[0] */
4992 			for (;;) {
4993 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4994 					goto out;
4995 				}
4996 				if (behavior == INTERSECT_NORMAL) {
4997 					if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
4998 						break;
4999 					}
5000 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5001 					/* no need of looping because indexes are unique */
5002 					break;
5003 				}
5004 			}
5005 		}
5006 	}
5007 out:
5008 	for (i = 0; i < arr_argc; i++) {
5009 		hash = Z_ARRVAL(args[i]);
5010 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5011 	}
5012 
5013 	PHP_ARRAY_CMP_FUNC_RESTORE();
5014 
5015 	efree(ptrs);
5016 	efree(lists);
5017 }
5018 /* }}} */
5019 
5020 /* {{{ 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. */
5021 PHP_FUNCTION(array_intersect_key)
5022 {
5023 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5024 }
5025 /* }}} */
5026 
5027 /* {{{ 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. */
5028 PHP_FUNCTION(array_intersect_ukey)
5029 {
5030 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5031 }
5032 /* }}} */
5033 
5034 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5035 PHP_FUNCTION(array_intersect)
5036 {
5037 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5038 }
5039 /* }}} */
5040 
5041 /* {{{ 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. */
5042 PHP_FUNCTION(array_uintersect)
5043 {
5044 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5045 }
5046 /* }}} */
5047 
5048 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5049 PHP_FUNCTION(array_intersect_assoc)
5050 {
5051 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5052 }
5053 /* }}} */
5054 
5055 /* {{{ 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. */
5056 PHP_FUNCTION(array_intersect_uassoc)
5057 {
5058 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5059 }
5060 /* }}} */
5061 
5062 /* {{{ 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. */
5063 PHP_FUNCTION(array_uintersect_assoc)
5064 {
5065 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5066 }
5067 /* }}} */
5068 
5069 /* {{{ 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. */
5070 PHP_FUNCTION(array_uintersect_uassoc)
5071 {
5072 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5073 }
5074 /* }}} */
5075 
5076 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5077 {
5078 	int argc, i;
5079 	zval *args;
5080 	int (*diff_data_compare_func)(zval *, zval *) = NULL;
5081 	bool ok;
5082 	zval *val, *data;
5083 	zend_string *key;
5084 	zend_ulong h;
5085 
5086 	/* Get the argument count */
5087 	argc = ZEND_NUM_ARGS();
5088 	if (data_compare_type == DIFF_COMP_DATA_USER) {
5089 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5090 			RETURN_THROWS();
5091 		}
5092 		diff_data_compare_func = zval_user_compare;
5093 	} else {
5094 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5095 			RETURN_THROWS();
5096 		}
5097 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5098 			diff_data_compare_func = zval_compare;
5099 		}
5100 	}
5101 
5102 	for (i = 0; i < argc; i++) {
5103 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5104 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5105 			RETURN_THROWS();
5106 		}
5107 	}
5108 
5109 	array_init(return_value);
5110 
5111 	/* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5112 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5113 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5114 			val = Z_REFVAL_P(val);
5115 		}
5116 		if (key == NULL) {
5117 			ok = 1;
5118 			for (i = 1; i < argc; i++) {
5119 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5120 					(!diff_data_compare_func ||
5121 					diff_data_compare_func(val, data) == 0)
5122 				) {
5123 					ok = 0;
5124 					break;
5125 				}
5126 			}
5127 			if (ok) {
5128 				Z_TRY_ADDREF_P(val);
5129 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5130 			}
5131 		} else {
5132 			ok = 1;
5133 			for (i = 1; i < argc; i++) {
5134 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5135 					(!diff_data_compare_func ||
5136 					diff_data_compare_func(val, data) == 0)
5137 				) {
5138 					ok = 0;
5139 					break;
5140 				}
5141 			}
5142 			if (ok) {
5143 				Z_TRY_ADDREF_P(val);
5144 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5145 			}
5146 		}
5147 	} ZEND_HASH_FOREACH_END();
5148 }
5149 /* }}} */
5150 
5151 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5152 {
5153 	zval *args = NULL;
5154 	HashTable *hash;
5155 	int arr_argc, i, c;
5156 	uint32_t idx;
5157 	Bucket **lists, *list, **ptrs, *p;
5158 	char *param_spec;
5159 	zend_fcall_info fci1, fci2;
5160 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5161 	zend_fcall_info *fci_key = NULL, *fci_data;
5162 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5163 	PHP_ARRAY_CMP_FUNC_VARS;
5164 
5165 	bucket_compare_func_t diff_key_compare_func;
5166 	bucket_compare_func_t diff_data_compare_func;
5167 
5168 	if (behavior == DIFF_NORMAL) {
5169 		diff_key_compare_func = php_array_key_compare_string_unstable;
5170 
5171 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5172 			/* array_diff */
5173 			param_spec = "+";
5174 			diff_data_compare_func = php_array_data_compare_string_unstable;
5175 		} else if (data_compare_type == DIFF_COMP_DATA_USER) {
5176 			/* array_udiff */
5177 			param_spec = "+f";
5178 			diff_data_compare_func = php_array_user_compare_unstable;
5179 		} else {
5180 			ZEND_ASSERT(0 && "Invalid data_compare_type");
5181 			return;
5182 		}
5183 
5184 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5185 			RETURN_THROWS();
5186 		}
5187 		fci_data = &fci1;
5188 		fci_data_cache = &fci1_cache;
5189 
5190 	} else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5191 		/* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5192 		 * no comparison of the data is done (part of DIFF_ASSOC) */
5193 
5194 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5195 			/* array_diff_assoc() or array_diff_key() */
5196 			param_spec = "+";
5197 			diff_key_compare_func = php_array_key_compare_string_unstable;
5198 			diff_data_compare_func = php_array_data_compare_string_unstable;
5199 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5200 			/* array_udiff_assoc() */
5201 			param_spec = "+f";
5202 			diff_key_compare_func = php_array_key_compare_string_unstable;
5203 			diff_data_compare_func = php_array_user_compare_unstable;
5204 			fci_data = &fci1;
5205 			fci_data_cache = &fci1_cache;
5206 		} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5207 			/* array_diff_uassoc() or array_diff_ukey() */
5208 			param_spec = "+f";
5209 			diff_key_compare_func = php_array_user_key_compare_unstable;
5210 			diff_data_compare_func = php_array_data_compare_string_unstable;
5211 			fci_key = &fci1;
5212 			fci_key_cache = &fci1_cache;
5213 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5214 			/* array_udiff_uassoc() */
5215 			param_spec = "+ff";
5216 			diff_key_compare_func = php_array_user_key_compare_unstable;
5217 			diff_data_compare_func = php_array_user_compare_unstable;
5218 			fci_data = &fci1;
5219 			fci_data_cache = &fci1_cache;
5220 			fci_key = &fci2;
5221 			fci_key_cache = &fci2_cache;
5222 		} else {
5223 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5224 			return;
5225 		}
5226 
5227 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5228 			RETURN_THROWS();
5229 		}
5230 
5231 	} else {
5232 		ZEND_ASSERT(0 && "Invalid behavior");
5233 		return;
5234 	}
5235 
5236 	PHP_ARRAY_CMP_FUNC_BACKUP();
5237 
5238 	/* for each argument, create and sort list with pointers to the hash buckets */
5239 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5240 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5241 
5242 	if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5243 		BG(user_compare_fci) = *fci_data;
5244 		BG(user_compare_fci_cache) = *fci_data_cache;
5245 	} else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5246 		BG(user_compare_fci) = *fci_key;
5247 		BG(user_compare_fci_cache) = *fci_key_cache;
5248 	}
5249 
5250 	for (i = 0; i < arr_argc; i++) {
5251 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5252 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5253 			arr_argc = i; /* only free up to i - 1 */
5254 			goto out;
5255 		}
5256 		hash = Z_ARRVAL(args[i]);
5257 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5258 		lists[i] = list;
5259 		ptrs[i] = list;
5260 		if (HT_IS_PACKED(hash)) {
5261 			zval *zv = hash->arPacked;
5262 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5263 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5264 				ZVAL_COPY_VALUE(&list->val, zv);
5265 				list->h = idx;
5266 				list->key = NULL;
5267 				list++;
5268 			}
5269 		} else {
5270 			p = hash->arData;
5271 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5272 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5273 				*list++ = *p;
5274 			}
5275 		}
5276 		ZVAL_UNDEF(&list->val);
5277 		if (hash->nNumOfElements > 1) {
5278 			if (behavior == DIFF_NORMAL) {
5279 				zend_sort((void *) lists[i], hash->nNumOfElements,
5280 						sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5281 						(swap_func_t)zend_hash_bucket_swap);
5282 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5283 				zend_sort((void *) lists[i], hash->nNumOfElements,
5284 						sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5285 						(swap_func_t)zend_hash_bucket_swap);
5286 			}
5287 		}
5288 	}
5289 
5290 	/* copy the argument array */
5291 	RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5292 
5293 	/* go through the lists and look for values of ptr[0] that are not in the others */
5294 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5295 		if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5296 			&&
5297 			key_compare_type == DIFF_COMP_KEY_USER
5298 		) {
5299 			BG(user_compare_fci) = *fci_key;
5300 			BG(user_compare_fci_cache) = *fci_key_cache;
5301 		}
5302 		c = 1;
5303 		for (i = 1; i < arr_argc; i++) {
5304 			Bucket *ptr = ptrs[i];
5305 			if (behavior == DIFF_NORMAL) {
5306 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5307 					ptrs[i]++;
5308 				}
5309 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5310 				while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5311 					ptr++;
5312 				}
5313 			}
5314 			if (!c) {
5315 				if (behavior == DIFF_NORMAL) {
5316 					if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5317 						ptrs[i]++;
5318 					}
5319 					break;
5320 				} else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
5321 					/* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5322 					 * data comparison is not needed - skipped. */
5323 					if (Z_TYPE(ptr->val) != IS_UNDEF) {
5324 						if (data_compare_type == DIFF_COMP_DATA_USER) {
5325 							BG(user_compare_fci) = *fci_data;
5326 							BG(user_compare_fci_cache) = *fci_data_cache;
5327 						}
5328 						if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5329 							/* the data is not the same */
5330 							c = -1;
5331 							if (key_compare_type == DIFF_COMP_KEY_USER) {
5332 								BG(user_compare_fci) = *fci_key;
5333 								BG(user_compare_fci_cache) = *fci_key_cache;
5334 							}
5335 						} else {
5336 							break;
5337 							/* we have found the element in other arrays thus we don't want it
5338 							 * in the return_value -> delete from there */
5339 						}
5340 					}
5341 				} else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5342 					/* the behavior here differs from INTERSECT_KEY in php_intersect
5343 					 * since in the "diff" case we have to remove the entry from
5344 					 * return_value while when doing intersection the entry must not
5345 					 * be deleted. */
5346 					break; /* remove the key */
5347 				}
5348 			}
5349 		}
5350 		if (!c) {
5351 			/* ptrs[0] in one of the other arguments */
5352 			/* delete all entries with value as ptrs[0] */
5353 			for (;;) {
5354 				p = ptrs[0];
5355 				if (p->key == NULL) {
5356 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5357 				} else {
5358 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5359 				}
5360 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5361 					goto out;
5362 				}
5363 				if (behavior == DIFF_NORMAL) {
5364 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5365 						break;
5366 					}
5367 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5368 					/* in this case no array_key_compare is needed */
5369 					break;
5370 				}
5371 			}
5372 		} else {
5373 			/* ptrs[0] in none of the other arguments */
5374 			/* skip all entries with value as ptrs[0] */
5375 			for (;;) {
5376 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5377 					goto out;
5378 				}
5379 				if (behavior == DIFF_NORMAL) {
5380 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5381 						break;
5382 					}
5383 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5384 					/* in this case no array_key_compare is needed */
5385 					break;
5386 				}
5387 			}
5388 		}
5389 	}
5390 out:
5391 	for (i = 0; i < arr_argc; i++) {
5392 		hash = Z_ARRVAL(args[i]);
5393 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5394 	}
5395 
5396 	PHP_ARRAY_CMP_FUNC_RESTORE();
5397 
5398 	efree(ptrs);
5399 	efree(lists);
5400 }
5401 /* }}} */
5402 
5403 /* {{{ 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. */
5404 PHP_FUNCTION(array_diff_key)
5405 {
5406 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5407 }
5408 /* }}} */
5409 
5410 /* {{{ 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. */
5411 PHP_FUNCTION(array_diff_ukey)
5412 {
5413 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5414 }
5415 /* }}} */
5416 
5417 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5418 PHP_FUNCTION(array_diff)
5419 {
5420 	zval *args;
5421 	int argc, i;
5422 	uint32_t num;
5423 	HashTable exclude;
5424 	zval *value;
5425 	zend_string *str, *tmp_str, *key;
5426 	zend_long idx;
5427 	zval dummy;
5428 
5429 	ZEND_PARSE_PARAMETERS_START(1, -1)
5430 		Z_PARAM_VARIADIC('+', args, argc)
5431 	ZEND_PARSE_PARAMETERS_END();
5432 
5433 	if (Z_TYPE(args[0]) != IS_ARRAY) {
5434 		zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
5435 		RETURN_THROWS();
5436 	}
5437 
5438 	num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5439 	if (num == 0) {
5440 		for (i = 1; i < argc; i++) {
5441 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5442 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5443 				RETURN_THROWS();
5444 			}
5445 		}
5446 		RETURN_EMPTY_ARRAY();
5447 	} else if (num == 1) {
5448 		int found = 0;
5449 		zend_string *search_str, *tmp_search_str;
5450 
5451 		value = NULL;
5452 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5453 			break;
5454 		} ZEND_HASH_FOREACH_END();
5455 
5456 		if (!value) {
5457 			for (i = 1; i < argc; i++) {
5458 				if (Z_TYPE(args[i]) != IS_ARRAY) {
5459 					zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5460 					RETURN_THROWS();
5461 				}
5462 			}
5463 			RETURN_EMPTY_ARRAY();
5464 		}
5465 
5466 		search_str = zval_get_tmp_string(value, &tmp_search_str);
5467 
5468 		for (i = 1; i < argc; i++) {
5469 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5470 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5471 				RETURN_THROWS();
5472 			}
5473 			if (!found) {
5474 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5475 					str = zval_get_tmp_string(value, &tmp_str);
5476 					if (zend_string_equals(search_str, str)) {
5477 						zend_tmp_string_release(tmp_str);
5478 						found = 1;
5479 						break;
5480 					}
5481 					zend_tmp_string_release(tmp_str);
5482 				} ZEND_HASH_FOREACH_END();
5483 			}
5484 		}
5485 
5486 		zend_tmp_string_release(tmp_search_str);
5487 
5488 		if (found) {
5489 			RETVAL_EMPTY_ARRAY();
5490 		} else {
5491 			ZVAL_COPY(return_value, &args[0]);
5492 		}
5493 		return;
5494 	}
5495 
5496 	/* count number of elements */
5497 	num = 0;
5498 	for (i = 1; i < argc; i++) {
5499 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5500 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5501 			RETURN_THROWS();
5502 		}
5503 		num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5504 	}
5505 
5506 	if (num == 0) {
5507 		ZVAL_COPY(return_value, &args[0]);
5508 		return;
5509 	}
5510 
5511 	ZVAL_NULL(&dummy);
5512 	/* create exclude map */
5513 	zend_hash_init(&exclude, num, NULL, NULL, 0);
5514 	for (i = 1; i < argc; i++) {
5515 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5516 			str = zval_get_tmp_string(value, &tmp_str);
5517 			zend_hash_add(&exclude, str, &dummy);
5518 			zend_tmp_string_release(tmp_str);
5519 		} ZEND_HASH_FOREACH_END();
5520 	}
5521 
5522 	/* copy all elements of first array that are not in exclude set */
5523 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5524 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5525 		str = zval_get_tmp_string(value, &tmp_str);
5526 		if (!zend_hash_exists(&exclude, str)) {
5527 			if (key) {
5528 				value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5529 			} else {
5530 				value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5531 			}
5532 			zval_add_ref(value);
5533 		}
5534 		zend_tmp_string_release(tmp_str);
5535 	} ZEND_HASH_FOREACH_END();
5536 
5537 	zend_hash_destroy(&exclude);
5538 }
5539 /* }}} */
5540 
5541 /* {{{ 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. */
5542 PHP_FUNCTION(array_udiff)
5543 {
5544 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5545 }
5546 /* }}} */
5547 
5548 /* {{{ 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 */
5549 PHP_FUNCTION(array_diff_assoc)
5550 {
5551 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5552 }
5553 /* }}} */
5554 
5555 /* {{{ 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. */
5556 PHP_FUNCTION(array_diff_uassoc)
5557 {
5558 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5559 }
5560 /* }}} */
5561 
5562 /* {{{ 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. */
5563 PHP_FUNCTION(array_udiff_assoc)
5564 {
5565 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5566 }
5567 /* }}} */
5568 
5569 /* {{{ 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. */
5570 PHP_FUNCTION(array_udiff_uassoc)
5571 {
5572 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5573 }
5574 /* }}} */
5575 
5576 #define MULTISORT_ORDER	0
5577 #define MULTISORT_TYPE	1
5578 #define MULTISORT_LAST	2
5579 
5580 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5581 {
5582 	Bucket *ab = *(Bucket **)a;
5583 	Bucket *bb = *(Bucket **)b;
5584 	int r;
5585 	zend_long result;
5586 
5587 	r = 0;
5588 	do {
5589 		result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5590 		if (result != 0) {
5591 			return result > 0 ? 1 : -1;
5592 		}
5593 		r++;
5594 	} while (Z_TYPE(ab[r].val) != IS_UNDEF);
5595 
5596 	return stable_sort_fallback(&ab[r], &bb[r]);
5597 }
5598 /* }}} */
5599 
5600 #define MULTISORT_ABORT				\
5601 	efree(func);	\
5602 	efree(arrays);					\
5603 	return;
5604 
5605 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5606 	Bucket *t;
5607 	Bucket **f = (Bucket **)p;
5608 	Bucket **g = (Bucket **)q;
5609 
5610 	t = *f;
5611 	*f = *g;
5612 	*g = t;
5613 }
5614 /* }}} */
5615 
5616 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5617 PHP_FUNCTION(array_multisort)
5618 {
5619 	zval*			args;
5620 	zval**			arrays;
5621 	Bucket**		indirect;
5622 	uint32_t            idx;
5623 	HashTable*		hash;
5624 	int				argc;
5625 	int				array_size;
5626 	int				num_arrays = 0;
5627 	int				parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
5628 	int				sort_order = PHP_SORT_ASC;
5629 	int				sort_type  = PHP_SORT_REGULAR;
5630 	int				i, k, n;
5631 	bucket_compare_func_t *func;
5632 
5633 	ZEND_PARSE_PARAMETERS_START(1, -1)
5634 		Z_PARAM_VARIADIC('+', args, argc)
5635 	ZEND_PARSE_PARAMETERS_END();
5636 
5637 	/* Allocate space for storing pointers to input arrays and sort flags. */
5638 	arrays = (zval **)ecalloc(argc, sizeof(zval *));
5639 	for (i = 0; i < MULTISORT_LAST; i++) {
5640 		parse_state[i] = 0;
5641 	}
5642 	func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
5643 
5644 	/* Here we go through the input arguments and parse them. Each one can
5645 	 * be either an array or a sort flag which follows an array. If not
5646 	 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5647 	 * accordingly. There can't be two sort flags of the same type after an
5648 	 * array, and the very first argument has to be an array. */
5649 	for (i = 0; i < argc; i++) {
5650 		zval *arg = &args[i];
5651 
5652 		ZVAL_DEREF(arg);
5653 		if (Z_TYPE_P(arg) == IS_ARRAY) {
5654 			SEPARATE_ARRAY(arg);
5655 			/* We see the next array, so we update the sort flags of
5656 			 * the previous array and reset the sort flags. */
5657 			if (i > 0) {
5658 				ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5659 				sort_order = PHP_SORT_ASC;
5660 				sort_type = PHP_SORT_REGULAR;
5661 			}
5662 			arrays[num_arrays++] = arg;
5663 
5664 			/* Next one may be an array or a list of sort flags. */
5665 			for (k = 0; k < MULTISORT_LAST; k++) {
5666 				parse_state[k] = 1;
5667 			}
5668 		} else if (Z_TYPE_P(arg) == IS_LONG) {
5669 			switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5670 				case PHP_SORT_ASC:
5671 				case PHP_SORT_DESC:
5672 					/* flag allowed here */
5673 					if (parse_state[MULTISORT_ORDER] == 1) {
5674 						/* Save the flag and make sure then next arg is not the current flag. */
5675 						sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5676 						parse_state[MULTISORT_ORDER] = 0;
5677 					} else {
5678 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5679 						MULTISORT_ABORT;
5680 					}
5681 					break;
5682 
5683 				case PHP_SORT_REGULAR:
5684 				case PHP_SORT_NUMERIC:
5685 				case PHP_SORT_STRING:
5686 				case PHP_SORT_NATURAL:
5687 				case PHP_SORT_LOCALE_STRING:
5688 					/* flag allowed here */
5689 					if (parse_state[MULTISORT_TYPE] == 1) {
5690 						/* Save the flag and make sure then next arg is not the current flag. */
5691 						sort_type = (int)Z_LVAL_P(arg);
5692 						parse_state[MULTISORT_TYPE] = 0;
5693 					} else {
5694 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5695 						MULTISORT_ABORT;
5696 					}
5697 					break;
5698 
5699 				default:
5700 					zend_argument_value_error(i + 1, "must be a valid sort flag");
5701 					MULTISORT_ABORT;
5702 					break;
5703 
5704 			}
5705 		} else {
5706 			zend_argument_type_error(i + 1, "must be an array or a sort flag");
5707 			MULTISORT_ABORT;
5708 		}
5709 	}
5710 	/* Take care of the last array sort flags. */
5711 	ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5712 
5713 	/* Make sure the arrays are of the same size. */
5714 	array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
5715 	for (i = 1; i < num_arrays; i++) {
5716 		if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
5717 			zend_value_error("Array sizes are inconsistent");
5718 			MULTISORT_ABORT;
5719 		}
5720 	}
5721 
5722 	/* If all arrays are empty we don't need to do anything. */
5723 	if (array_size < 1) {
5724 		efree(func);
5725 		efree(arrays);
5726 		RETURN_TRUE;
5727 	}
5728 
5729 	/* Create the indirection array. This array is of size MxN, where
5730 	 * M is the number of entries in each input array and N is the number
5731 	 * of the input arrays + 1. The last column is UNDEF to indicate the end
5732 	 * of the row. It also stores the original position for stable sorting. */
5733 	indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
5734 	for (i = 0; i < array_size; i++) {
5735 		indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
5736 	}
5737 	for (i = 0; i < num_arrays; i++) {
5738 		k = 0;
5739 		if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
5740 			zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
5741 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
5742 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5743 				ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
5744 				indirect[k][i].h = idx;
5745 				indirect[k][i].key = NULL;
5746 				k++;
5747 			}
5748 		} else {
5749 			Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
5750 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
5751 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5752 				indirect[k][i] = *p;
5753 				k++;
5754 			}
5755 		}
5756 	}
5757 	for (k = 0; k < array_size; k++) {
5758 		ZVAL_UNDEF(&indirect[k][num_arrays].val);
5759 		Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
5760 	}
5761 
5762 	/* Do the actual sort magic - bada-bim, bada-boom. */
5763 	zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
5764 	if (EG(exception)) {
5765 		goto clean_up;
5766 	}
5767 
5768 	/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
5769 	for (i = 0; i < num_arrays; i++) {
5770 		hash = Z_ARRVAL_P(arrays[i]);
5771 		hash->nNumUsed = array_size;
5772 		hash->nNextFreeElement = array_size;
5773 		hash->nInternalPointer = 0;
5774 		if (HT_IS_PACKED(hash)) {
5775 			for (k = 0; k < array_size; k++) {
5776 				ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
5777 			}
5778 		} else {
5779 			int repack = 1;
5780 
5781 			for (n = 0, k = 0; k < array_size; k++) {
5782 				hash->arData[k] = indirect[k][i];
5783 				if (hash->arData[k].key == NULL) {
5784 					hash->arData[k].h = n++;
5785 				} else {
5786 					repack = 0;
5787 				}
5788 			}
5789 			if (repack) {
5790 				zend_hash_to_packed(hash);
5791 			} else {
5792 				zend_hash_rehash(hash);
5793 			}
5794 		}
5795 	}
5796 	RETVAL_TRUE;
5797 
5798 clean_up:
5799 	for (i = 0; i < array_size; i++) {
5800 		efree(indirect[i]);
5801 	}
5802 	efree(indirect);
5803 	efree(func);
5804 	efree(arrays);
5805 }
5806 /* }}} */
5807 
5808 /* {{{ php_array_pick_keys */
5809 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)
5810 {
5811 	HashTable *ht = Z_ARRVAL_P(input);
5812 	uint32_t num_avail = zend_hash_num_elements(ht);
5813 	zend_long i, randval;
5814 	zend_string *string_key;
5815 	zend_ulong num_key;
5816 	zval *zv;
5817 	Bucket *b;
5818 	zend_bitset bitset;
5819 	int negative_bitset = 0;
5820 	uint32_t bitset_len;
5821 	ALLOCA_FLAG(use_heap);
5822 
5823 	if (num_avail == 0) {
5824 		if (!silent) {
5825 			zend_argument_value_error(1, "cannot be empty");
5826 		}
5827 		return false;
5828 	}
5829 
5830 	if (num_req == 1) {
5831 		if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
5832 			/* If less than 1/2 of elements are used, don't sample. Instead search for a
5833 			 * specific offset using linear scan. */
5834 			i = 0;
5835 			randval = algo->range(status, 0, num_avail - 1);
5836 			if (EG(exception)) {
5837 				return false;
5838 			}
5839 			ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
5840 				if (i == randval) {
5841 					if (string_key) {
5842 						ZVAL_STR_COPY(retval, string_key);
5843 					} else {
5844 						ZVAL_LONG(retval, num_key);
5845 					}
5846 					return true;
5847 				}
5848 				i++;
5849 			} ZEND_HASH_FOREACH_END();
5850 		}
5851 
5852 		/* Sample random buckets until we hit one that is not empty.
5853 		 * The worst case probability of hitting an empty element is 1-1/2. The worst case
5854 		 * probability of hitting N empty elements in a row is (1-1/2)**N.
5855 		 * For N=10 this becomes smaller than 0.1%. */
5856 		if (HT_IS_PACKED(ht)) {
5857 			do {
5858 				randval = algo->range(status, 0, ht->nNumUsed - 1);
5859 				if (EG(exception)) {
5860 					return false;
5861 				}
5862 				zv = &ht->arPacked[randval];
5863 				if (!Z_ISUNDEF_P(zv)) {
5864 					ZVAL_LONG(retval, randval);
5865 					return true;
5866 				}
5867 			} while (true);
5868 		} else {
5869 			do {
5870 				randval = algo->range(status, 0, ht->nNumUsed - 1);
5871 				if (EG(exception)) {
5872 					return false;
5873 				}
5874 				b = &ht->arData[randval];
5875 				if (!Z_ISUNDEF(b->val)) {
5876 					if (b->key) {
5877 						ZVAL_STR_COPY(retval, b->key);
5878 					} else {
5879 						ZVAL_LONG(retval, b->h);
5880 					}
5881 					return true;
5882 				}
5883 			} while (true);
5884 		}
5885 	}
5886 
5887 	if (num_req <= 0 || num_req > num_avail) {
5888 		if (!silent) {
5889 			zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
5890 		}
5891 		return false;
5892 	}
5893 
5894 	/* Make the return value an array only if we need to pass back more than one result. */
5895 	array_init_size(retval, (uint32_t) num_req);
5896 	if (num_req > (num_avail >> 1)) {
5897 		negative_bitset = 1;
5898 		num_req = num_avail - num_req;
5899 	}
5900 
5901 	bitset_len = zend_bitset_len(num_avail);
5902 	bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
5903 	zend_bitset_clear(bitset, bitset_len);
5904 
5905 	i = num_req;
5906 	int failures = 0;
5907 	while (i) {
5908 		randval = algo->range(status, 0, num_avail - 1);
5909 		if (EG(exception)) {
5910 			goto fail;
5911 		}
5912 		if (zend_bitset_in(bitset, randval)) {
5913 			/* Use PHP_RANDOM_RANGE_ATTEMPTS instead of the hardcoded 50 for 8.3+. */
5914 			if (++failures > 50) {
5915 				if (!silent) {
5916 					zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", 50);
5917 				}
5918 
5919 				goto fail;
5920 			}
5921 		} else {
5922 			zend_bitset_incl(bitset, randval);
5923 			i--;
5924 			failures = 0;
5925 		}
5926 	}
5927 
5928 	zend_hash_real_init_packed(Z_ARRVAL_P(retval));
5929 	ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
5930 		/* We can't use zend_hash_index_find()
5931 		 * because the array may have string keys or gaps. */
5932 		ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
5933 			if (zend_bitset_in(bitset, i) ^ negative_bitset) {
5934 				if (string_key) {
5935 					ZEND_HASH_FILL_SET_STR_COPY(string_key);
5936 				} else {
5937 					ZEND_HASH_FILL_SET_LONG(num_key);
5938 				}
5939 				ZEND_HASH_FILL_NEXT();
5940 			}
5941 			i++;
5942 		} ZEND_HASH_FOREACH_END();
5943 	} ZEND_HASH_FILL_END();
5944 
5945 	free_alloca(bitset, use_heap);
5946 
5947 	return true;
5948 
5949  fail:
5950 	free_alloca(bitset, use_heap);
5951 
5952 	return false;
5953 }
5954 /* }}} */
5955 
5956 /* {{{ Return key/keys for random entry/entries in the array */
5957 PHP_FUNCTION(array_rand)
5958 {
5959 	zval *input;
5960 	zend_long num_req = 1;
5961 
5962 	ZEND_PARSE_PARAMETERS_START(1, 2)
5963 		Z_PARAM_ARRAY(input)
5964 		Z_PARAM_OPTIONAL
5965 		Z_PARAM_LONG(num_req)
5966 	ZEND_PARSE_PARAMETERS_END();
5967 
5968 	if (!php_array_pick_keys(
5969 			php_random_default_algo(),
5970 			php_random_default_status(),
5971 			input,
5972 			num_req,
5973 			return_value,
5974 			false)
5975 	) {
5976 		RETURN_THROWS();
5977 	}
5978 }
5979 /* }}} */
5980 
5981 /* {{{ Returns the sum of the array entries */
5982 PHP_FUNCTION(array_sum)
5983 {
5984 	zval *input,
5985 		 *entry,
5986 		 entry_n;
5987 
5988 	ZEND_PARSE_PARAMETERS_START(1, 1)
5989 		Z_PARAM_ARRAY(input)
5990 	ZEND_PARSE_PARAMETERS_END();
5991 
5992 	ZVAL_LONG(return_value, 0);
5993 
5994 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
5995 		if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
5996 			continue;
5997 		}
5998 		ZVAL_COPY(&entry_n, entry);
5999 		convert_scalar_to_number(&entry_n);
6000 		fast_add_function(return_value, return_value, &entry_n);
6001 	} ZEND_HASH_FOREACH_END();
6002 }
6003 /* }}} */
6004 
6005 /* {{{ Returns the product of the array entries */
6006 PHP_FUNCTION(array_product)
6007 {
6008 	zval *input,
6009 		 *entry,
6010 		 entry_n;
6011 	double dval;
6012 
6013 	ZEND_PARSE_PARAMETERS_START(1, 1)
6014 		Z_PARAM_ARRAY(input)
6015 	ZEND_PARSE_PARAMETERS_END();
6016 
6017 	ZVAL_LONG(return_value, 1);
6018 	if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
6019 		return;
6020 	}
6021 
6022 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
6023 		if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
6024 			continue;
6025 		}
6026 		ZVAL_COPY(&entry_n, entry);
6027 		convert_scalar_to_number(&entry_n);
6028 
6029 		if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
6030 			dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
6031 			if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
6032 				Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
6033 				continue;
6034 			}
6035 		}
6036 		convert_to_double(return_value);
6037 		convert_to_double(&entry_n);
6038 		Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
6039 	} ZEND_HASH_FOREACH_END();
6040 }
6041 /* }}} */
6042 
6043 /* {{{ Iteratively reduce the array to a single value via the callback. */
6044 PHP_FUNCTION(array_reduce)
6045 {
6046 	zval *input;
6047 	zval args[2];
6048 	zval *operand;
6049 	zval retval;
6050 	zend_fcall_info fci;
6051 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6052 	zval *initial = NULL;
6053 	HashTable *htbl;
6054 
6055 	ZEND_PARSE_PARAMETERS_START(2, 3)
6056 		Z_PARAM_ARRAY(input)
6057 		Z_PARAM_FUNC(fci, fci_cache)
6058 		Z_PARAM_OPTIONAL
6059 		Z_PARAM_ZVAL(initial)
6060 	ZEND_PARSE_PARAMETERS_END();
6061 
6062 
6063 	if (ZEND_NUM_ARGS() > 2) {
6064 		ZVAL_COPY(return_value, initial);
6065 	} else {
6066 		ZVAL_NULL(return_value);
6067 	}
6068 
6069 	/* (zval **)input points to an element of argument stack
6070 	 * the base pointer of which is subject to change.
6071 	 * thus we need to keep the pointer to the hashtable for safety */
6072 	htbl = Z_ARRVAL_P(input);
6073 
6074 	if (zend_hash_num_elements(htbl) == 0) {
6075 		return;
6076 	}
6077 
6078 	fci.retval = &retval;
6079 	fci.param_count = 2;
6080 
6081 	ZEND_HASH_FOREACH_VAL(htbl, operand) {
6082 		ZVAL_COPY_VALUE(&args[0], return_value);
6083 		ZVAL_COPY(&args[1], operand);
6084 		fci.params = args;
6085 
6086 		if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6087 			zval_ptr_dtor(&args[1]);
6088 			zval_ptr_dtor(&args[0]);
6089 			ZVAL_COPY_VALUE(return_value, &retval);
6090 			if (UNEXPECTED(Z_ISREF_P(return_value))) {
6091 				zend_unwrap_reference(return_value);
6092 			}
6093 		} else {
6094 			zval_ptr_dtor(&args[1]);
6095 			zval_ptr_dtor(&args[0]);
6096 			RETURN_NULL();
6097 		}
6098 	} ZEND_HASH_FOREACH_END();
6099 }
6100 /* }}} */
6101 
6102 /* {{{ Filters elements from the array via the callback. */
6103 PHP_FUNCTION(array_filter)
6104 {
6105 	zval *array;
6106 	zval *operand;
6107 	zval *key;
6108 	zval args[2];
6109 	zval retval;
6110 	bool have_callback = 0;
6111 	zend_long use_type = 0;
6112 	zend_string *string_key;
6113 	zend_fcall_info fci = empty_fcall_info;
6114 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6115 	zend_ulong num_key;
6116 
6117 	ZEND_PARSE_PARAMETERS_START(1, 3)
6118 		Z_PARAM_ARRAY(array)
6119 		Z_PARAM_OPTIONAL
6120 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6121 		Z_PARAM_LONG(use_type)
6122 	ZEND_PARSE_PARAMETERS_END();
6123 
6124 	if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6125 		RETVAL_EMPTY_ARRAY();
6126 		return;
6127 	}
6128 	array_init(return_value);
6129 
6130 	if (ZEND_FCI_INITIALIZED(fci)) {
6131 		have_callback = 1;
6132 		fci.retval = &retval;
6133 		if (use_type == ARRAY_FILTER_USE_BOTH) {
6134 			fci.param_count = 2;
6135 			key = &args[1];
6136 		} else {
6137 			fci.param_count = 1;
6138 			key = &args[0];
6139 		}
6140 	}
6141 
6142 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6143 		if (have_callback) {
6144 			if (use_type) {
6145 				/* Set up the key */
6146 				if (!string_key) {
6147 					ZVAL_LONG(key, num_key);
6148 				} else {
6149 					ZVAL_STR_COPY(key, string_key);
6150 				}
6151 			}
6152 			if (use_type != ARRAY_FILTER_USE_KEY) {
6153 				ZVAL_COPY(&args[0], operand);
6154 			}
6155 			fci.params = args;
6156 
6157 			if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6158 				int retval_true;
6159 
6160 				zval_ptr_dtor(&args[0]);
6161 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6162 					zval_ptr_dtor(&args[1]);
6163 				}
6164 				retval_true = zend_is_true(&retval);
6165 				zval_ptr_dtor(&retval);
6166 				if (!retval_true) {
6167 					continue;
6168 				}
6169 			} else {
6170 				zval_ptr_dtor(&args[0]);
6171 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6172 					zval_ptr_dtor(&args[1]);
6173 				}
6174 				return;
6175 			}
6176 		} else if (!zend_is_true(operand)) {
6177 			continue;
6178 		}
6179 
6180 		if (string_key) {
6181 			operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6182 		} else {
6183 			operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6184 		}
6185 		zval_add_ref(operand);
6186 	} ZEND_HASH_FOREACH_END();
6187 }
6188 /* }}} */
6189 
6190 /* {{{ Applies the callback to the elements in given arrays. */
6191 PHP_FUNCTION(array_map)
6192 {
6193 	zval *arrays = NULL;
6194 	int n_arrays = 0;
6195 	zval result;
6196 	zend_fcall_info fci = empty_fcall_info;
6197 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6198 	int i;
6199 	uint32_t k, maxlen = 0;
6200 
6201 	ZEND_PARSE_PARAMETERS_START(2, -1)
6202 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6203 		Z_PARAM_VARIADIC('+', arrays, n_arrays)
6204 	ZEND_PARSE_PARAMETERS_END();
6205 
6206 	if (n_arrays == 1) {
6207 		zend_ulong num_key;
6208 		zend_string *str_key;
6209 		zval *zv, arg;
6210 		int ret;
6211 
6212 		if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6213 			zend_argument_type_error(2, "must be of type array, %s given", zend_zval_type_name(&arrays[0]));
6214 			RETURN_THROWS();
6215 		}
6216 		maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6217 
6218 		/* Short-circuit: if no callback and only one array, just return it. */
6219 		if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6220 			ZVAL_COPY(return_value, &arrays[0]);
6221 			return;
6222 		}
6223 
6224 		array_init_size(return_value, maxlen);
6225 		zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6226 
6227 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6228 			fci.retval = &result;
6229 			fci.param_count = 1;
6230 			fci.params = &arg;
6231 
6232 			ZVAL_COPY(&arg, zv);
6233 			ret = zend_call_function(&fci, &fci_cache);
6234 			i_zval_ptr_dtor(&arg);
6235 			if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6236 				zend_array_destroy(Z_ARR_P(return_value));
6237 				RETURN_NULL();
6238 			}
6239 			if (str_key) {
6240 				_zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6241 			} else {
6242 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6243 			}
6244 		} ZEND_HASH_FOREACH_END();
6245 	} else {
6246 		uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6247 
6248 		for (i = 0; i < n_arrays; i++) {
6249 			if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6250 				zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_type_name(&arrays[i]));
6251 				efree(array_pos);
6252 				RETURN_THROWS();
6253 			}
6254 			if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6255 				maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6256 			}
6257 		}
6258 
6259 		array_init_size(return_value, maxlen);
6260 
6261 		if (!ZEND_FCI_INITIALIZED(fci)) {
6262 			zval zv;
6263 
6264 			/* We iterate through all the arrays at once. */
6265 			for (k = 0; k < maxlen; k++) {
6266 
6267 				/* If no callback, the result will be an array, consisting of current
6268 				 * entries from all arrays. */
6269 				array_init_size(&result, n_arrays);
6270 
6271 				for (i = 0; i < n_arrays; i++) {
6272 					/* If this array still has elements, add the current one to the
6273 					 * parameter list, otherwise use null value. */
6274 					uint32_t pos = array_pos[i];
6275 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6276 						while (1) {
6277 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6278 								ZVAL_NULL(&zv);
6279 								break;
6280 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6281 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6282 								array_pos[i] = pos + 1;
6283 								break;
6284 							}
6285 							pos++;
6286 						}
6287 					} else {
6288 						while (1) {
6289 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6290 								ZVAL_NULL(&zv);
6291 								break;
6292 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6293 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6294 								array_pos[i] = pos + 1;
6295 								break;
6296 							}
6297 							pos++;
6298 						}
6299 					}
6300 					zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6301 				}
6302 
6303 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6304 			}
6305 		} else {
6306 			zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6307 
6308 			/* We iterate through all the arrays at once. */
6309 			for (k = 0; k < maxlen; k++) {
6310 				for (i = 0; i < n_arrays; i++) {
6311 					/* If this array still has elements, add the current one to the
6312 					 * parameter list, otherwise use null value. */
6313 					uint32_t pos = array_pos[i];
6314 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6315 						while (1) {
6316 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6317 								ZVAL_NULL(&params[i]);
6318 								break;
6319 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6320 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6321 								array_pos[i] = pos + 1;
6322 								break;
6323 							}
6324 							pos++;
6325 						}
6326 					} else {
6327 						while (1) {
6328 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6329 								ZVAL_NULL(&params[i]);
6330 								break;
6331 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6332 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6333 								array_pos[i] = pos + 1;
6334 								break;
6335 							}
6336 							pos++;
6337 						}
6338 					}
6339 				}
6340 
6341 				fci.retval = &result;
6342 				fci.param_count = n_arrays;
6343 				fci.params = params;
6344 
6345 				if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6346 					efree(array_pos);
6347 					zend_array_destroy(Z_ARR_P(return_value));
6348 					for (i = 0; i < n_arrays; i++) {
6349 						zval_ptr_dtor(&params[i]);
6350 					}
6351 					efree(params);
6352 					RETURN_NULL();
6353 				} else {
6354 					for (i = 0; i < n_arrays; i++) {
6355 						zval_ptr_dtor(&params[i]);
6356 					}
6357 				}
6358 
6359 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6360 			}
6361 
6362 			efree(params);
6363 		}
6364 		efree(array_pos);
6365 	}
6366 }
6367 /* }}} */
6368 
6369 /* {{{ Checks if the given key or index exists in the array */
6370 PHP_FUNCTION(array_key_exists)
6371 {
6372 	zval *key;
6373 	HashTable *ht;
6374 
6375 	ZEND_PARSE_PARAMETERS_START(2, 2)
6376 		Z_PARAM_ZVAL(key)
6377 		Z_PARAM_ARRAY_HT(ht)
6378 	ZEND_PARSE_PARAMETERS_END();
6379 
6380 	switch (Z_TYPE_P(key)) {
6381 		case IS_STRING:
6382 			RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6383 			break;
6384 		case IS_LONG:
6385 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6386 			break;
6387 		case IS_NULL:
6388 			RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6389 			break;
6390 		case IS_DOUBLE:
6391 			RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6392 			break;
6393 		case IS_FALSE:
6394 			RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6395 			break;
6396 		case IS_TRUE:
6397 			RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6398 			break;
6399 		case IS_RESOURCE:
6400 			zend_use_resource_as_offset(key);
6401 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6402 			break;
6403 		default:
6404 			zend_argument_type_error(1, "must be a valid array offset type");
6405 			break;
6406 	}
6407 }
6408 /* }}} */
6409 
6410 /* {{{ Split array into chunks */
6411 PHP_FUNCTION(array_chunk)
6412 {
6413 	int num_in;
6414 	zend_long size, current = 0;
6415 	zend_string *str_key;
6416 	zend_ulong num_key;
6417 	bool preserve_keys = 0;
6418 	zval *input = NULL;
6419 	zval chunk;
6420 	zval *entry;
6421 
6422 	ZEND_PARSE_PARAMETERS_START(2, 3)
6423 		Z_PARAM_ARRAY(input)
6424 		Z_PARAM_LONG(size)
6425 		Z_PARAM_OPTIONAL
6426 		Z_PARAM_BOOL(preserve_keys)
6427 	ZEND_PARSE_PARAMETERS_END();
6428 
6429 	/* Do bounds checking for size parameter. */
6430 	if (size < 1) {
6431 		zend_argument_value_error(2, "must be greater than 0");
6432 		RETURN_THROWS();
6433 	}
6434 
6435 	num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6436 
6437 	if (size > num_in) {
6438 		if (num_in == 0) {
6439 			RETVAL_EMPTY_ARRAY();
6440 			return;
6441 		}
6442 		size = num_in;
6443 	}
6444 
6445 	array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6446 
6447 	ZVAL_UNDEF(&chunk);
6448 
6449 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6450 		/* If new chunk, create and initialize it. */
6451 		if (Z_TYPE(chunk) == IS_UNDEF) {
6452 			array_init_size(&chunk, (uint32_t)size);
6453 		}
6454 
6455 		/* Add entry to the chunk, preserving keys if necessary. */
6456 		if (preserve_keys) {
6457 			if (str_key) {
6458 				entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6459 			} else {
6460 				entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6461 			}
6462 		} else {
6463 			entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6464 		}
6465 		zval_add_ref(entry);
6466 
6467 		/* If reached the chunk size, add it to the result array, and reset the
6468 		 * pointer. */
6469 		if (!(++current % size)) {
6470 			add_next_index_zval(return_value, &chunk);
6471 			ZVAL_UNDEF(&chunk);
6472 		}
6473 	} ZEND_HASH_FOREACH_END();
6474 
6475 	/* Add the final chunk if there is one. */
6476 	if (Z_TYPE(chunk) != IS_UNDEF) {
6477 		add_next_index_zval(return_value, &chunk);
6478 	}
6479 }
6480 /* }}} */
6481 
6482 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6483 PHP_FUNCTION(array_combine)
6484 {
6485 	HashTable *values, *keys;
6486 	uint32_t pos_values = 0;
6487 	zval *entry_keys, *entry_values;
6488 	int num_keys, num_values;
6489 
6490 	ZEND_PARSE_PARAMETERS_START(2, 2)
6491 		Z_PARAM_ARRAY_HT(keys)
6492 		Z_PARAM_ARRAY_HT(values)
6493 	ZEND_PARSE_PARAMETERS_END();
6494 
6495 	num_keys = zend_hash_num_elements(keys);
6496 	num_values = zend_hash_num_elements(values);
6497 
6498 	if (num_keys != num_values) {
6499 		zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6500 		RETURN_THROWS();
6501 	}
6502 
6503 	if (!num_keys) {
6504 		RETURN_EMPTY_ARRAY();
6505 	}
6506 
6507 	array_init_size(return_value, num_keys);
6508 	ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6509 		while (1) {
6510 			if (pos_values >= values->nNumUsed) {
6511 				break;
6512 			}
6513 			entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6514 			if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6515 				if (Z_TYPE_P(entry_keys) == IS_LONG) {
6516 					entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6517 						Z_LVAL_P(entry_keys), entry_values);
6518 				} else {
6519 					zend_string *tmp_key;
6520 					zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6521 					entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6522 						key, entry_values);
6523 					zend_tmp_string_release(tmp_key);
6524 				}
6525 				zval_add_ref(entry_values);
6526 				pos_values++;
6527 				break;
6528 			}
6529 			pos_values++;
6530 		}
6531 	} ZEND_HASH_FOREACH_END();
6532 }
6533 /* }}} */
6534