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