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