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