xref: /PHP-8.3/ext/standard/array.c (revision 24a29492)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Andi Gutmans <andi@php.net>                                 |
14    |          Zeev Suraski <zeev@php.net>                                 |
15    |          Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Andrei Zmievski <andrei@php.net>                            |
17    |          Stig Venaas <venaas@php.net>                                |
18    |          Jason Greene <jason@php.net>                                |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef PHP_WIN32
31 #include "win32/unistd.h"
32 #endif
33 #include "zend_globals.h"
34 #include "zend_interfaces.h"
35 #include "php_globals.h"
36 #include "php_array.h"
37 #include "basic_functions.h"
38 #include "php_string.h"
39 #include "php_math.h"
40 #include "zend_smart_str.h"
41 #include "zend_bitset.h"
42 #include "zend_exceptions.h"
43 #include "ext/spl/spl_array.h"
44 #include "ext/random/php_random.h"
45 
46 /* {{{ defines */
47 
48 #define DIFF_NORMAL			1
49 #define DIFF_KEY			2
50 #define DIFF_ASSOC			6
51 #define DIFF_COMP_DATA_NONE    -1
52 #define DIFF_COMP_DATA_INTERNAL 0
53 #define DIFF_COMP_DATA_USER     1
54 #define DIFF_COMP_KEY_INTERNAL  0
55 #define DIFF_COMP_KEY_USER      1
56 
57 #define INTERSECT_NORMAL		1
58 #define INTERSECT_KEY			2
59 #define INTERSECT_ASSOC			6
60 #define INTERSECT_COMP_DATA_NONE    -1
61 #define INTERSECT_COMP_DATA_INTERNAL 0
62 #define INTERSECT_COMP_DATA_USER     1
63 #define INTERSECT_COMP_KEY_INTERNAL  0
64 #define INTERSECT_COMP_KEY_USER      1
65 /* }}} */
66 
ZEND_DECLARE_MODULE_GLOBALS(array)67 ZEND_DECLARE_MODULE_GLOBALS(array)
68 
69 /* {{{ php_array_init_globals */
70 static void php_array_init_globals(zend_array_globals *array_globals)
71 {
72 	memset(array_globals, 0, sizeof(zend_array_globals));
73 }
74 /* }}} */
75 
PHP_MINIT_FUNCTION(array)76 PHP_MINIT_FUNCTION(array) /* {{{ */
77 {
78 	ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
79 
80 	return SUCCESS;
81 }
82 /* }}} */
83 
PHP_MSHUTDOWN_FUNCTION(array)84 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
85 {
86 #ifdef ZTS
87 	ts_free_id(array_globals_id);
88 #endif
89 
90 	return SUCCESS;
91 }
92 /* }}} */
93 
stable_sort_fallback(Bucket * a,Bucket * b)94 static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
95 	if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
96 		return 1;
97 	} else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
98 		return -1;
99 	} else {
100 		return 0;
101 	}
102 }
103 
104 #define RETURN_STABLE_SORT(a, b, result) do { \
105 	int _result = (result); \
106 	if (EXPECTED(_result)) { \
107 		return _result; \
108 	} \
109 	return stable_sort_fallback((a), (b)); \
110 } while (0)
111 
112 /* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
113 #define DEFINE_SORT_VARIANTS(name) \
114 	static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
115 		return php_array_##name##_unstable_i(a, b); \
116 	} \
117 	static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
118 		RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
119 	} \
120 	static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
121 		return php_array_##name##_unstable(a, b) * -1; \
122 	} \
123 	static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
124 		RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
125 	} \
126 
php_array_key_compare_unstable_i(Bucket * f,Bucket * s)127 static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
128 {
129 	zval first;
130 	zval second;
131 
132 	if (f->key == NULL && s->key == NULL) {
133 		return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
134 	} else if (f->key && s->key) {
135 		return zendi_smart_strcmp(f->key, s->key);
136 	}
137 	if (f->key) {
138 		ZVAL_STR(&first, f->key);
139 	} else {
140 		ZVAL_LONG(&first, f->h);
141 	}
142 	if (s->key) {
143 		ZVAL_STR(&second, s->key);
144 	} else {
145 		ZVAL_LONG(&second, s->h);
146 	}
147 	return zend_compare(&first, &second);
148 }
149 /* }}} */
150 
php_array_key_compare_numeric_unstable_i(Bucket * f,Bucket * s)151 static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
152 {
153 	if (f->key == NULL && s->key == NULL) {
154 		return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
155 	} else {
156 		double d1, d2;
157 		if (f->key) {
158 			d1 = zend_strtod(f->key->val, NULL);
159 		} else {
160 			d1 = (double)(zend_long)f->h;
161 		}
162 		if (s->key) {
163 			d2 = zend_strtod(s->key->val, NULL);
164 		} else {
165 			d2 = (double)(zend_long)s->h;
166 		}
167 		return ZEND_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_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
709 	} else {
710 		zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
711 	}
712 
713 	RETURN_TRUE;
714 }
715 /* }}} */
716 
717 /* {{{ Sort an array using natural sort */
PHP_FUNCTION(natsort)718 PHP_FUNCTION(natsort)
719 {
720 	php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
721 }
722 /* }}} */
723 
724 /* {{{ Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)725 PHP_FUNCTION(natcasesort)
726 {
727 	php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
728 }
729 /* }}} */
730 
731 /* {{{ Sort an array and maintain index association */
PHP_FUNCTION(asort)732 PHP_FUNCTION(asort)
733 {
734 	zval *array;
735 	zend_long sort_type = PHP_SORT_REGULAR;
736 	bucket_compare_func_t cmp;
737 
738 	ZEND_PARSE_PARAMETERS_START(1, 2)
739 		Z_PARAM_ARRAY_EX(array, 0, 1)
740 		Z_PARAM_OPTIONAL
741 		Z_PARAM_LONG(sort_type)
742 	ZEND_PARSE_PARAMETERS_END();
743 
744 	cmp = php_get_data_compare_func(sort_type, 0);
745 
746 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
747 
748 	RETURN_TRUE;
749 }
750 /* }}} */
751 
752 /* {{{ Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)753 PHP_FUNCTION(arsort)
754 {
755 	zval *array;
756 	zend_long sort_type = PHP_SORT_REGULAR;
757 	bucket_compare_func_t cmp;
758 
759 	ZEND_PARSE_PARAMETERS_START(1, 2)
760 		Z_PARAM_ARRAY_EX(array, 0, 1)
761 		Z_PARAM_OPTIONAL
762 		Z_PARAM_LONG(sort_type)
763 	ZEND_PARSE_PARAMETERS_END();
764 
765 	cmp = php_get_data_compare_func(sort_type, 1);
766 
767 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
768 
769 	RETURN_TRUE;
770 }
771 /* }}} */
772 
773 /* {{{ Sort an array */
PHP_FUNCTION(sort)774 PHP_FUNCTION(sort)
775 {
776 	zval *array;
777 	zend_long sort_type = PHP_SORT_REGULAR;
778 	bucket_compare_func_t cmp;
779 
780 	ZEND_PARSE_PARAMETERS_START(1, 2)
781 		Z_PARAM_ARRAY_EX(array, 0, 1)
782 		Z_PARAM_OPTIONAL
783 		Z_PARAM_LONG(sort_type)
784 	ZEND_PARSE_PARAMETERS_END();
785 
786 	cmp = php_get_data_compare_func(sort_type, 0);
787 
788 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
789 
790 	RETURN_TRUE;
791 }
792 /* }}} */
793 
794 /* {{{ Sort an array in reverse order */
PHP_FUNCTION(rsort)795 PHP_FUNCTION(rsort)
796 {
797 	zval *array;
798 	zend_long sort_type = PHP_SORT_REGULAR;
799 	bucket_compare_func_t cmp;
800 
801 	ZEND_PARSE_PARAMETERS_START(1, 2)
802 		Z_PARAM_ARRAY_EX(array, 0, 1)
803 		Z_PARAM_OPTIONAL
804 		Z_PARAM_LONG(sort_type)
805 	ZEND_PARSE_PARAMETERS_END();
806 
807 	cmp = php_get_data_compare_func(sort_type, 1);
808 
809 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
810 
811 	RETURN_TRUE;
812 }
813 /* }}} */
814 
php_array_user_compare_unstable(Bucket * f,Bucket * s)815 static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
816 {
817 	zval args[2];
818 	zval retval;
819 	bool call_failed;
820 
821 	ZVAL_COPY(&args[0], &f->val);
822 	ZVAL_COPY(&args[1], &s->val);
823 
824 	BG(user_compare_fci).param_count = 2;
825 	BG(user_compare_fci).params = args;
826 	BG(user_compare_fci).retval = &retval;
827 	call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
828 	zval_ptr_dtor(&args[1]);
829 	zval_ptr_dtor(&args[0]);
830 	if (UNEXPECTED(call_failed)) {
831 		return 0;
832 	}
833 
834 	if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
835 		if (!ARRAYG(compare_deprecation_thrown)) {
836 			php_error_docref(NULL, E_DEPRECATED,
837 				"Returning bool from comparison function is deprecated, "
838 				"return an integer less than, equal to, or greater than zero");
839 			ARRAYG(compare_deprecation_thrown) = 1;
840 		}
841 
842 		if (Z_TYPE(retval) == IS_FALSE) {
843 			/* Retry with swapped operands. */
844 			ZVAL_COPY(&args[0], &s->val);
845 			ZVAL_COPY(&args[1], &f->val);
846 			call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
847 			zval_ptr_dtor(&args[1]);
848 			zval_ptr_dtor(&args[0]);
849 			if (call_failed) {
850 				return 0;
851 			}
852 
853 			zend_long ret = zval_get_long(&retval);
854 			zval_ptr_dtor(&retval);
855 			return -ZEND_NORMALIZE_BOOL(ret);
856 		}
857 	}
858 
859 	zend_long ret = zval_get_long(&retval);
860 	zval_ptr_dtor(&retval);
861 	return ZEND_NORMALIZE_BOOL(ret);
862 }
863 /* }}} */
864 
php_array_user_compare(Bucket * a,Bucket * b)865 static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
866 {
867 	RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
868 }
869 /* }}} */
870 
871 #define PHP_ARRAY_CMP_FUNC_VARS \
872 	zend_fcall_info old_user_compare_fci; \
873 	zend_fcall_info_cache old_user_compare_fci_cache \
874 
875 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
876 	old_user_compare_fci = BG(user_compare_fci); \
877 	old_user_compare_fci_cache = BG(user_compare_fci_cache); \
878 	ARRAYG(compare_deprecation_thrown) = 0; \
879 	BG(user_compare_fci_cache) = empty_fcall_info_cache; \
880 
881 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
882 	BG(user_compare_fci) = old_user_compare_fci; \
883 	BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
884 
php_usort(INTERNAL_FUNCTION_PARAMETERS,bucket_compare_func_t compare_func,bool renumber)885 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, bool renumber) /* {{{ */
886 {
887 	zval *array;
888 	zend_array *arr;
889 	PHP_ARRAY_CMP_FUNC_VARS;
890 
891 	PHP_ARRAY_CMP_FUNC_BACKUP();
892 
893 	ZEND_PARSE_PARAMETERS_START(2, 2)
894 		Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
895 		Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
896 	ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
897 
898 	arr = Z_ARR_P(array);
899 	if (zend_hash_num_elements(arr) == 0)  {
900 		PHP_ARRAY_CMP_FUNC_RESTORE();
901 		RETURN_TRUE;
902 	}
903 
904 	/* Copy array, so the in-place modifications will not be visible to the callback function */
905 	arr = zend_array_dup(arr);
906 
907 	zend_hash_sort(arr, compare_func, renumber);
908 
909 	zval garbage;
910 	ZVAL_COPY_VALUE(&garbage, array);
911 	ZVAL_ARR(array, arr);
912 	zval_ptr_dtor(&garbage);
913 
914 	PHP_ARRAY_CMP_FUNC_RESTORE();
915 	RETURN_TRUE;
916 }
917 /* }}} */
918 
919 /* {{{ Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)920 PHP_FUNCTION(usort)
921 {
922 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
923 }
924 /* }}} */
925 
926 /* {{{ Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)927 PHP_FUNCTION(uasort)
928 {
929 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
930 }
931 /* }}} */
932 
php_array_user_key_compare_unstable(Bucket * f,Bucket * s)933 static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
934 {
935 	zval args[2];
936 	zval retval;
937 	bool call_failed;
938 
939 	if (f->key == NULL) {
940 		ZVAL_LONG(&args[0], f->h);
941 	} else {
942 		ZVAL_STR_COPY(&args[0], f->key);
943 	}
944 	if (s->key == NULL) {
945 		ZVAL_LONG(&args[1], s->h);
946 	} else {
947 		ZVAL_STR_COPY(&args[1], s->key);
948 	}
949 
950 	BG(user_compare_fci).param_count = 2;
951 	BG(user_compare_fci).params = args;
952 	BG(user_compare_fci).retval = &retval;
953 	call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
954 	zval_ptr_dtor(&args[1]);
955 	zval_ptr_dtor(&args[0]);
956 	if (UNEXPECTED(call_failed)) {
957 		return 0;
958 	}
959 
960 	if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
961 		if (!ARRAYG(compare_deprecation_thrown)) {
962 			php_error_docref(NULL, E_DEPRECATED,
963 				"Returning bool from comparison function is deprecated, "
964 				"return an integer less than, equal to, or greater than zero");
965 			ARRAYG(compare_deprecation_thrown) = 1;
966 		}
967 
968 		if (Z_TYPE(retval) == IS_FALSE) {
969 			/* Retry with swapped operands. */
970 			if (s->key == NULL) {
971 				ZVAL_LONG(&args[0], s->h);
972 			} else {
973 				ZVAL_STR_COPY(&args[0], s->key);
974 			}
975 			if (f->key == NULL) {
976 				ZVAL_LONG(&args[1], f->h);
977 			} else {
978 				ZVAL_STR_COPY(&args[1], f->key);
979 			}
980 
981 			call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
982 			zval_ptr_dtor(&args[1]);
983 			zval_ptr_dtor(&args[0]);
984 			if (call_failed) {
985 				return 0;
986 			}
987 
988 			zend_long ret = zval_get_long(&retval);
989 			zval_ptr_dtor(&retval);
990 			return -ZEND_NORMALIZE_BOOL(ret);
991 		}
992 	}
993 
994 	zend_long result = zval_get_long(&retval);
995 	zval_ptr_dtor(&retval);
996 	return ZEND_NORMALIZE_BOOL(result);
997 }
998 /* }}} */
999 
php_array_user_key_compare(Bucket * a,Bucket * b)1000 static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */
1001 {
1002 	RETURN_STABLE_SORT(a, b, php_array_user_key_compare_unstable(a, b));
1003 }
1004 /* }}} */
1005 
1006 /* {{{ Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)1007 PHP_FUNCTION(uksort)
1008 {
1009 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1010 }
1011 /* }}} */
1012 
get_ht_for_iap(zval * zv,bool separate)1013 static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
1014 	if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
1015 		return Z_ARRVAL_P(zv);
1016 	}
1017 
1018 	ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
1019 	php_error_docref(NULL, E_DEPRECATED,
1020 		"Calling %s() on an object is deprecated", get_active_function_name());
1021 
1022 	zend_object *zobj = Z_OBJ_P(zv);
1023 	if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
1024 		if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
1025 			GC_DELREF(zobj->properties);
1026 		}
1027 		zobj->properties = zend_array_dup(zobj->properties);
1028 	}
1029 	return zobj->handlers->get_properties(zobj);
1030 }
1031 
1032 /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1033 PHP_FUNCTION(end)
1034 {
1035 	zval *array_zv;
1036 	zval *entry;
1037 
1038 	ZEND_PARSE_PARAMETERS_START(1, 1)
1039 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1040 	ZEND_PARSE_PARAMETERS_END();
1041 
1042 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1043 	if (zend_hash_num_elements(array) == 0) {
1044 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1045 		RETURN_FALSE;
1046 	}
1047 	zend_hash_internal_pointer_end(array);
1048 
1049 	if (USED_RET()) {
1050 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1051 			RETURN_FALSE;
1052 		}
1053 
1054 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1055 			entry = Z_INDIRECT_P(entry);
1056 		}
1057 
1058 		RETURN_COPY_DEREF(entry);
1059 	}
1060 }
1061 /* }}} */
1062 
1063 /* {{{ Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1064 PHP_FUNCTION(prev)
1065 {
1066 	zval *array_zv;
1067 	zval *entry;
1068 
1069 	ZEND_PARSE_PARAMETERS_START(1, 1)
1070 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1071 	ZEND_PARSE_PARAMETERS_END();
1072 
1073 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1074 	if (zend_hash_num_elements(array) == 0) {
1075 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1076 		RETURN_FALSE;
1077 	}
1078 	zend_hash_move_backwards(array);
1079 
1080 	if (USED_RET()) {
1081 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1082 			RETURN_FALSE;
1083 		}
1084 
1085 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1086 			entry = Z_INDIRECT_P(entry);
1087 		}
1088 
1089 		RETURN_COPY_DEREF(entry);
1090 	}
1091 }
1092 /* }}} */
1093 
1094 /* {{{ Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1095 PHP_FUNCTION(next)
1096 {
1097 	zval *array_zv;
1098 	zval *entry;
1099 
1100 	ZEND_PARSE_PARAMETERS_START(1, 1)
1101 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1102 	ZEND_PARSE_PARAMETERS_END();
1103 
1104 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1105 	if (zend_hash_num_elements(array) == 0) {
1106 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1107 		RETURN_FALSE;
1108 	}
1109 	zend_hash_move_forward(array);
1110 
1111 	if (USED_RET()) {
1112 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1113 			RETURN_FALSE;
1114 		}
1115 
1116 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1117 			entry = Z_INDIRECT_P(entry);
1118 		}
1119 
1120 		RETURN_COPY_DEREF(entry);
1121 	}
1122 }
1123 /* }}} */
1124 
1125 /* {{{ Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1126 PHP_FUNCTION(reset)
1127 {
1128 	zval *array_zv;
1129 	zval *entry;
1130 
1131 	ZEND_PARSE_PARAMETERS_START(1, 1)
1132 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1133 	ZEND_PARSE_PARAMETERS_END();
1134 
1135 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1136 	if (zend_hash_num_elements(array) == 0) {
1137 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1138 		RETURN_FALSE;
1139 	}
1140 	zend_hash_internal_pointer_reset(array);
1141 
1142 	if (USED_RET()) {
1143 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1144 			RETURN_FALSE;
1145 		}
1146 
1147 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1148 			entry = Z_INDIRECT_P(entry);
1149 		}
1150 
1151 		RETURN_COPY_DEREF(entry);
1152 	}
1153 }
1154 /* }}} */
1155 
1156 /* {{{ Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1157 PHP_FUNCTION(current)
1158 {
1159 	zval *array_zv;
1160 	zval *entry;
1161 
1162 	ZEND_PARSE_PARAMETERS_START(1, 1)
1163 		Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1164 	ZEND_PARSE_PARAMETERS_END();
1165 
1166 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1167 	if ((entry = zend_hash_get_current_data(array)) == NULL) {
1168 		RETURN_FALSE;
1169 	}
1170 
1171 	if (Z_TYPE_P(entry) == IS_INDIRECT) {
1172 		entry = Z_INDIRECT_P(entry);
1173 	}
1174 
1175 	RETURN_COPY_DEREF(entry);
1176 }
1177 /* }}} */
1178 
1179 /* {{{ Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1180 PHP_FUNCTION(key)
1181 {
1182 	zval *array_zv;
1183 
1184 	ZEND_PARSE_PARAMETERS_START(1, 1)
1185 		Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1186 	ZEND_PARSE_PARAMETERS_END();
1187 
1188 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1189 	zend_hash_get_current_key_zval(array, return_value);
1190 }
1191 /* }}} */
1192 
php_data_compare(const void * f,const void * s)1193 static int php_data_compare(const void *f, const void *s) /* {{{ */
1194 {
1195 	return zend_compare((zval*)f, (zval*)s);
1196 }
1197 /* }}} */
1198 
1199 /* {{{
1200  * proto mixed min(array values)
1201  * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1202    Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1203 PHP_FUNCTION(min)
1204 {
1205 	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 				SEPARATE_ZVAL(dest_entry);
3920 				dest_zval = dest_entry;
3921 
3922 				if (Z_TYPE_P(dest_zval) == IS_NULL) {
3923 					convert_to_array(dest_zval);
3924 					add_next_index_null(dest_zval);
3925 				} else {
3926 					convert_to_array(dest_zval);
3927 				}
3928 				ZVAL_UNDEF(&tmp);
3929 				if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3930 					ZVAL_COPY(&tmp, src_zval);
3931 					convert_to_array(&tmp);
3932 					src_zval = &tmp;
3933 				}
3934 				if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3935 					if (thash) {
3936 						GC_TRY_PROTECT_RECURSION(thash);
3937 					}
3938 					ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3939 					if (thash) {
3940 						GC_TRY_UNPROTECT_RECURSION(thash);
3941 					}
3942 					if (!ret) {
3943 						return 0;
3944 					}
3945 				} else {
3946 					Z_TRY_ADDREF_P(src_zval);
3947 					zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3948 					if (EXPECTED(!zv)) {
3949 						Z_TRY_DELREF_P(src_zval);
3950 						zend_cannot_add_element();
3951 						return 0;
3952 					}
3953 				}
3954 				zval_ptr_dtor(&tmp);
3955 			} else {
3956 				zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3957 				zval_add_ref(zv);
3958 			}
3959 		} else {
3960 			zval *zv = zend_hash_next_index_insert(dest, src_entry);
3961 			if (UNEXPECTED(!zv)) {
3962 				zend_cannot_add_element();
3963 				return 0;
3964 			}
3965 			zval_add_ref(zv);
3966 		}
3967 	} ZEND_HASH_FOREACH_END();
3968 	return 1;
3969 }
3970 /* }}} */
3971 
3972 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3973 {
3974 	zval *src_entry;
3975 	zend_string *string_key;
3976 
3977 	if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
3978 		zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
3979 		ZEND_HASH_FILL_PACKED(dest) {
3980 			ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
3981 				if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
3982 					UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
3983 					src_entry = Z_REFVAL_P(src_entry);
3984 				}
3985 				Z_TRY_ADDREF_P(src_entry);
3986 				ZEND_HASH_FILL_ADD(src_entry);
3987 			} ZEND_HASH_FOREACH_END();
3988 		} ZEND_HASH_FILL_END();
3989 	} else {
3990 		ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3991 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3992 				Z_REFCOUNT_P(src_entry) == 1)) {
3993 				src_entry = Z_REFVAL_P(src_entry);
3994 			}
3995 			Z_TRY_ADDREF_P(src_entry);
3996 			if (string_key) {
3997 				zend_hash_update(dest, string_key, src_entry);
3998 			} else {
3999 				zend_hash_next_index_insert_new(dest, src_entry);
4000 			}
4001 		} ZEND_HASH_FOREACH_END();
4002 	}
4003 	return 1;
4004 }
4005 /* }}} */
4006 
4007 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4008 {
4009 	zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4010 	zend_string *string_key;
4011 	zend_ulong num_key;
4012 	int ret;
4013 
4014 	ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4015 		src_zval = src_entry;
4016 		ZVAL_DEREF(src_zval);
4017 		if (string_key) {
4018 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4019 				(dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4020 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
4021 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4022 
4023 				zval *zv = zend_hash_update(dest, string_key, src_entry);
4024 				zval_add_ref(zv);
4025 				continue;
4026 			}
4027 		} else {
4028 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4029 				(dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4030 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
4031 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4032 
4033 				zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4034 				zval_add_ref(zv);
4035 				continue;
4036 			}
4037 		}
4038 
4039 		dest_zval = dest_entry;
4040 		ZVAL_DEREF(dest_zval);
4041 		if (Z_IS_RECURSIVE_P(dest_zval) ||
4042 			Z_IS_RECURSIVE_P(src_zval) ||
4043 			(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))) {
4044 			zend_throw_error(NULL, "Recursion detected");
4045 			return 0;
4046 		}
4047 
4048 		ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4049 		SEPARATE_ZVAL(dest_entry);
4050 		dest_zval = dest_entry;
4051 
4052 		if (Z_REFCOUNTED_P(dest_zval)) {
4053 			Z_PROTECT_RECURSION_P(dest_zval);
4054 		}
4055 		if (Z_REFCOUNTED_P(src_zval)) {
4056 			Z_PROTECT_RECURSION_P(src_zval);
4057 		}
4058 
4059 		ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4060 
4061 		if (Z_REFCOUNTED_P(dest_zval)) {
4062 			Z_UNPROTECT_RECURSION_P(dest_zval);
4063 		}
4064 		if (Z_REFCOUNTED_P(src_zval)) {
4065 			Z_UNPROTECT_RECURSION_P(src_zval);
4066 		}
4067 
4068 		if (!ret) {
4069 			return 0;
4070 		}
4071 	} ZEND_HASH_FOREACH_END();
4072 
4073 	return 1;
4074 }
4075 /* }}} */
4076 
4077 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4078 {
4079 	zval *args = NULL;
4080 	zval *arg;
4081 	uint32_t argc, i;
4082 	HashTable *dest;
4083 
4084 	ZEND_PARSE_PARAMETERS_START(1, -1)
4085 		Z_PARAM_VARIADIC('+', args, argc)
4086 	ZEND_PARSE_PARAMETERS_END();
4087 
4088 
4089 	for (i = 0; i < argc; i++) {
4090 		zval *arg = args + i;
4091 
4092 		if (Z_TYPE_P(arg) != IS_ARRAY) {
4093 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4094 			RETURN_THROWS();
4095 		}
4096 	}
4097 
4098 	/* copy first array if necessary */
4099 	arg = args;
4100 	bool in_place = zend_may_modify_arg_in_place(arg);
4101 	if (in_place) {
4102 		dest = Z_ARRVAL_P(arg);
4103 	} else {
4104 		dest = zend_array_dup(Z_ARRVAL_P(arg));
4105 	}
4106 
4107 	ZVAL_ARR(return_value, dest);
4108 
4109 	if (recursive) {
4110 		for (i = 1; i < argc; i++) {
4111 			arg = args + i;
4112 			php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4113 		}
4114 	} else {
4115 		for (i = 1; i < argc; i++) {
4116 			arg = args + i;
4117 			zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4118 		}
4119 	}
4120 
4121 	if (in_place) {
4122 		GC_ADDREF(dest);
4123 	}
4124 }
4125 /* }}} */
4126 
4127 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4128 {
4129 	zval *args = NULL;
4130 	zval *arg;
4131 	uint32_t argc, i;
4132 	zval *src_entry;
4133 	HashTable *src, *dest;
4134 	uint32_t count = 0;
4135 
4136 	ZEND_PARSE_PARAMETERS_START(0, -1)
4137 		Z_PARAM_VARIADIC('+', args, argc)
4138 	ZEND_PARSE_PARAMETERS_END();
4139 
4140 	if (argc == 0) {
4141 		RETURN_EMPTY_ARRAY();
4142 	}
4143 
4144 	for (i = 0; i < argc; i++) {
4145 		zval *arg = args + i;
4146 
4147 		if (Z_TYPE_P(arg) != IS_ARRAY) {
4148 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4149 			RETURN_THROWS();
4150 		}
4151 		count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4152 	}
4153 
4154 	if (argc == 2) {
4155 		zval *ret = NULL;
4156 
4157 		if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4158 			ret = &args[1];
4159 		} else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4160 			ret = &args[0];
4161 		}
4162 		if (ret) {
4163 			if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4164 				if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4165 					ZVAL_COPY(return_value, ret);
4166 					return;
4167 				}
4168 			} else {
4169 				bool copy = 1;
4170 				zend_string *string_key;
4171 
4172 				ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4173 					if (!string_key) {
4174 						copy = 0;
4175 						break;
4176 					}
4177 				} ZEND_HASH_FOREACH_END();
4178 				if (copy) {
4179 					ZVAL_COPY(return_value, ret);
4180 					return;
4181 				}
4182 			}
4183 		}
4184 	}
4185 
4186 	arg = args;
4187 	src  = Z_ARRVAL_P(arg);
4188 	/* copy first array if necessary */
4189 	bool in_place = false;
4190 	if (HT_IS_PACKED(src)) {
4191 		/* Note: If it has holes, it might get sequentialized */
4192 		if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4193 			dest = src;
4194 			in_place = true;
4195 			ZVAL_ARR(return_value, dest);
4196 		} else {
4197 			array_init_size(return_value, count);
4198 			dest = Z_ARRVAL_P(return_value);
4199 
4200 			zend_hash_real_init_packed(dest);
4201 			ZEND_HASH_FILL_PACKED(dest) {
4202 				ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4203 					if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4204 						Z_REFCOUNT_P(src_entry) == 1)) {
4205 						src_entry = Z_REFVAL_P(src_entry);
4206 					}
4207 					Z_TRY_ADDREF_P(src_entry);
4208 					ZEND_HASH_FILL_ADD(src_entry);
4209 				} ZEND_HASH_FOREACH_END();
4210 			} ZEND_HASH_FILL_END();
4211 		}
4212 	} else {
4213 		array_init_size(return_value, count);
4214 		dest = Z_ARRVAL_P(return_value);
4215 
4216 		zend_string *string_key;
4217 		zend_hash_real_init_mixed(dest);
4218 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4219 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4220 				Z_REFCOUNT_P(src_entry) == 1)) {
4221 				src_entry = Z_REFVAL_P(src_entry);
4222 			}
4223 			Z_TRY_ADDREF_P(src_entry);
4224 			if (EXPECTED(string_key)) {
4225 				_zend_hash_append(dest, string_key, src_entry);
4226 			} else {
4227 				zend_hash_next_index_insert_new(dest, src_entry);
4228 			}
4229 		} ZEND_HASH_FOREACH_END();
4230 	}
4231 	if (recursive) {
4232 		for (i = 1; i < argc; i++) {
4233 			arg = args + i;
4234 			php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4235 		}
4236 	} else {
4237 		for (i = 1; i < argc; i++) {
4238 			arg = args + i;
4239 			php_array_merge(dest, Z_ARRVAL_P(arg));
4240 		}
4241 	}
4242 
4243 	if (in_place) {
4244 		GC_ADDREF(dest);
4245 	}
4246 }
4247 /* }}} */
4248 
4249 /* {{{ Merges elements from passed arrays into one array */
4250 PHP_FUNCTION(array_merge)
4251 {
4252 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4253 }
4254 /* }}} */
4255 
4256 /* {{{ Recursively merges elements from passed arrays into one array */
4257 PHP_FUNCTION(array_merge_recursive)
4258 {
4259 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4260 }
4261 /* }}} */
4262 
4263 /* {{{ Replaces elements from passed arrays into one array */
4264 PHP_FUNCTION(array_replace)
4265 {
4266 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4267 }
4268 /* }}} */
4269 
4270 /* {{{ Recursively replaces elements from passed arrays into one array */
4271 PHP_FUNCTION(array_replace_recursive)
4272 {
4273 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4274 }
4275 /* }}} */
4276 
4277 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4278 PHP_FUNCTION(array_keys)
4279 {
4280 	zval *input,				/* Input array */
4281 	     *search_value = NULL,	/* Value to search for */
4282 	     *entry,				/* An entry in the input array */
4283 	       new_val;				/* New value */
4284 	bool strict = 0;		/* do strict comparison */
4285 	zend_ulong num_idx;
4286 	zend_string *str_idx;
4287 	zend_array *arrval;
4288 	zend_ulong elem_count;
4289 
4290 	ZEND_PARSE_PARAMETERS_START(1, 3)
4291 		Z_PARAM_ARRAY(input)
4292 		Z_PARAM_OPTIONAL
4293 		Z_PARAM_ZVAL(search_value)
4294 		Z_PARAM_BOOL(strict)
4295 	ZEND_PARSE_PARAMETERS_END();
4296 	arrval = Z_ARRVAL_P(input);
4297 	elem_count = zend_hash_num_elements(arrval);
4298 
4299 	/* Base case: empty input */
4300 	if (!elem_count) {
4301 		RETURN_COPY(input);
4302 	}
4303 
4304 	/* Initialize return array */
4305 	if (search_value != NULL) {
4306 		array_init(return_value);
4307 
4308 		if (strict) {
4309 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4310 				ZVAL_DEREF(entry);
4311 				if (fast_is_identical_function(search_value, entry)) {
4312 					if (str_idx) {
4313 						ZVAL_STR_COPY(&new_val, str_idx);
4314 					} else {
4315 						ZVAL_LONG(&new_val, num_idx);
4316 					}
4317 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4318 				}
4319 			} ZEND_HASH_FOREACH_END();
4320 		} else {
4321 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4322 				if (fast_equal_check_function(search_value, entry)) {
4323 					if (str_idx) {
4324 						ZVAL_STR_COPY(&new_val, str_idx);
4325 					} else {
4326 						ZVAL_LONG(&new_val, num_idx);
4327 					}
4328 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4329 				}
4330 			} ZEND_HASH_FOREACH_END();
4331 		}
4332 	} else {
4333 		array_init_size(return_value, elem_count);
4334 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4335 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4336 			if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4337 				/* Optimistic case: range(0..n-1) for vector-like packed array */
4338 				zend_ulong lval = 0;
4339 
4340 				for (; lval < elem_count; ++lval) {
4341 					ZEND_HASH_FILL_SET_LONG(lval);
4342 					ZEND_HASH_FILL_NEXT();
4343 				}
4344 			} else {
4345 				/* Go through input array and add keys to the return array */
4346 				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4347 					if (str_idx) {
4348 						ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4349 					} else {
4350 						ZEND_HASH_FILL_SET_LONG(num_idx);
4351 					}
4352 					ZEND_HASH_FILL_NEXT();
4353 				} ZEND_HASH_FOREACH_END();
4354 			}
4355 		} ZEND_HASH_FILL_END();
4356 	}
4357 }
4358 /* }}} */
4359 
4360 /* {{{ Get the key of the first element of the array */
4361 PHP_FUNCTION(array_key_first)
4362 {
4363 	zval *stack;    /* Input stack */
4364 
4365 	ZEND_PARSE_PARAMETERS_START(1, 1)
4366 		Z_PARAM_ARRAY(stack)
4367 	ZEND_PARSE_PARAMETERS_END();
4368 
4369 	HashTable *target_hash = Z_ARRVAL_P (stack);
4370 	HashPosition pos = 0;
4371 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4372 }
4373 /* }}} */
4374 
4375 /* {{{ Get the key of the last element of the array */
4376 PHP_FUNCTION(array_key_last)
4377 {
4378 	zval *stack;    /* Input stack */
4379 	HashPosition pos;
4380 
4381 	ZEND_PARSE_PARAMETERS_START(1, 1)
4382 		Z_PARAM_ARRAY(stack)
4383 	ZEND_PARSE_PARAMETERS_END();
4384 
4385 	HashTable *target_hash = Z_ARRVAL_P (stack);
4386 	zend_hash_internal_pointer_end_ex(target_hash, &pos);
4387 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4388 }
4389 /* }}} */
4390 
4391 /* {{{ Return just the values from the input array */
4392 PHP_FUNCTION(array_values)
4393 {
4394 	zval	 *input;		/* Input array */
4395 	zend_array *arrval;
4396 	zend_long arrlen;
4397 
4398 	ZEND_PARSE_PARAMETERS_START(1, 1)
4399 		Z_PARAM_ARRAY(input)
4400 	ZEND_PARSE_PARAMETERS_END();
4401 
4402 	arrval = Z_ARRVAL_P(input);
4403 
4404 	/* Return empty input as is */
4405 	arrlen = zend_hash_num_elements(arrval);
4406 	if (!arrlen) {
4407 		RETURN_EMPTY_ARRAY();
4408 	}
4409 
4410 	/* Return vector-like packed arrays as-is */
4411 	if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4412 		arrval->nNextFreeElement == arrlen) {
4413 		RETURN_COPY(input);
4414 	}
4415 
4416 	RETURN_ARR(zend_array_to_list(arrval));
4417 }
4418 /* }}} */
4419 
4420 /* {{{ Return the value as key and the frequency of that value in input as value */
4421 PHP_FUNCTION(array_count_values)
4422 {
4423 	zval	*input,		/* Input array */
4424 			*entry,		/* An entry in the input array */
4425 			*tmp;
4426 	HashTable *myht;
4427 
4428 	ZEND_PARSE_PARAMETERS_START(1, 1)
4429 		Z_PARAM_ARRAY(input)
4430 	ZEND_PARSE_PARAMETERS_END();
4431 
4432 	/* Initialize return array */
4433 	array_init(return_value);
4434 
4435 	/* Go through input array and add values to the return array */
4436 	myht = Z_ARRVAL_P(input);
4437 	ZEND_HASH_FOREACH_VAL(myht, entry) {
4438 		ZVAL_DEREF(entry);
4439 		if (Z_TYPE_P(entry) == IS_LONG) {
4440 			if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4441 				zval data;
4442 				ZVAL_LONG(&data, 1);
4443 				zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4444 			} else {
4445 				Z_LVAL_P(tmp)++;
4446 			}
4447 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4448 			if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4449 				zval data;
4450 				ZVAL_LONG(&data, 1);
4451 				zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4452 			} else {
4453 				Z_LVAL_P(tmp)++;
4454 			}
4455 		} else {
4456 			php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4457 		}
4458 	} ZEND_HASH_FOREACH_END();
4459 }
4460 /* }}} */
4461 
4462 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4463 {
4464 	zval *prop = NULL;
4465 
4466 	if (Z_TYPE_P(data) == IS_OBJECT) {
4467 		zend_string *tmp_str;
4468 		/* If name is an integer convert integer to string */
4469 		if (name_str == NULL) {
4470 			tmp_str = zend_long_to_str(name_long);
4471 		} else {
4472 			tmp_str = zend_string_copy(name_str);
4473 		}
4474 		/* The has_property check is first performed in "exists" mode (which returns true for
4475 		 * properties that are null but exist) and then in "has" mode to handle objects that
4476 		 * implement __isset (which is not called in "exists" mode). */
4477 		if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4478 				|| Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4479 			prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4480 			if (prop) {
4481 				ZVAL_DEREF(prop);
4482 				if (prop != rv) {
4483 					Z_TRY_ADDREF_P(prop);
4484 				}
4485 			}
4486 		}
4487 		zend_string_release(tmp_str);
4488 	} else if (Z_TYPE_P(data) == IS_ARRAY) {
4489 		/* Name is a string */
4490 		if (name_str != NULL) {
4491 			prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4492 		} else {
4493 			prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4494 		}
4495 		if (prop) {
4496 			ZVAL_DEREF(prop);
4497 			Z_TRY_ADDREF_P(prop);
4498 		}
4499 	}
4500 
4501 	return prop;
4502 }
4503 /* }}} */
4504 
4505 /* {{{ Return the values from a single column in the input array, identified by the
4506    value_key and optionally indexed by the index_key */
4507 PHP_FUNCTION(array_column)
4508 {
4509 	HashTable *input;
4510 	zval *colval, *data, rv;
4511 	zend_string *column_str = NULL;
4512 	zend_long column_long = 0;
4513 	bool column_is_null = 0;
4514 	zend_string *index_str = NULL;
4515 	zend_long index_long = 0;
4516 	bool index_is_null = 1;
4517 
4518 	ZEND_PARSE_PARAMETERS_START(2, 3)
4519 		Z_PARAM_ARRAY_HT(input)
4520 		Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4521 		Z_PARAM_OPTIONAL
4522 		Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4523 	ZEND_PARSE_PARAMETERS_END();
4524 
4525 	void* cache_slot_column[3] = { NULL, NULL, NULL };
4526 	void* cache_slot_index[3] = { NULL, NULL, NULL };
4527 
4528 	array_init_size(return_value, zend_hash_num_elements(input));
4529 	/* Index param is not passed */
4530 	if (index_is_null) {
4531 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4532 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4533 			ZEND_HASH_FOREACH_VAL(input, data) {
4534 				ZVAL_DEREF(data);
4535 				if (column_is_null) {
4536 					Z_TRY_ADDREF_P(data);
4537 					colval = data;
4538 				} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4539 					continue;
4540 				}
4541 				ZEND_HASH_FILL_ADD(colval);
4542 			} ZEND_HASH_FOREACH_END();
4543 		} ZEND_HASH_FILL_END();
4544 	} else {
4545 		ZEND_HASH_FOREACH_VAL(input, data) {
4546 			ZVAL_DEREF(data);
4547 
4548 			if (column_is_null) {
4549 				Z_TRY_ADDREF_P(data);
4550 				colval = data;
4551 			} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4552 				continue;
4553 			}
4554 
4555 			zval rv;
4556 			zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4557 			if (keyval) {
4558 				array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4559 				zval_ptr_dtor(colval);
4560 				zval_ptr_dtor(keyval);
4561 			} else {
4562 				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4563 			}
4564 		} ZEND_HASH_FOREACH_END();
4565 	}
4566 }
4567 /* }}} */
4568 
4569 /* {{{ Return input as a new array with the order of the entries reversed */
4570 PHP_FUNCTION(array_reverse)
4571 {
4572 	zval	 *input,				/* Input array */
4573 			 *entry;				/* An entry in the input array */
4574 	zend_string *string_key;
4575 	zend_ulong	  num_key;
4576 	bool preserve_keys = 0;	/* whether to preserve keys */
4577 
4578 	ZEND_PARSE_PARAMETERS_START(1, 2)
4579 		Z_PARAM_ARRAY(input)
4580 		Z_PARAM_OPTIONAL
4581 		Z_PARAM_BOOL(preserve_keys)
4582 	ZEND_PARSE_PARAMETERS_END();
4583 
4584 	/* Initialize return array */
4585 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4586 	if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4587 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4588 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4589 			ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4590 				if (UNEXPECTED(Z_ISREF_P(entry) &&
4591 					Z_REFCOUNT_P(entry) == 1)) {
4592 					entry = Z_REFVAL_P(entry);
4593 				}
4594 				Z_TRY_ADDREF_P(entry);
4595 				ZEND_HASH_FILL_ADD(entry);
4596 			} ZEND_HASH_FOREACH_END();
4597 		} ZEND_HASH_FILL_END();
4598 	} else {
4599 		ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4600 			if (string_key) {
4601 				entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4602 			} else {
4603 				if (preserve_keys) {
4604 					entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4605 				} else {
4606 					entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4607 				}
4608 			}
4609 			zval_add_ref(entry);
4610 		} ZEND_HASH_FOREACH_END();
4611 	}
4612 }
4613 /* }}} */
4614 
4615 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4616 PHP_FUNCTION(array_pad)
4617 {
4618 	zval  *input;		/* Input array */
4619 	zval  *pad_value;	/* Padding value obviously */
4620 	zend_long pad_size;		/* Size to pad to */
4621 	zend_long pad_size_abs;	/* Absolute value of pad_size */
4622 	zend_long input_size;		/* Size of the input array */
4623 	zend_long num_pads;		/* How many pads do we need */
4624 	zend_long i;
4625 	zend_string *key;
4626 	zval *value;
4627 
4628 	ZEND_PARSE_PARAMETERS_START(3, 3)
4629 		Z_PARAM_ARRAY(input)
4630 		Z_PARAM_LONG(pad_size)
4631 		Z_PARAM_ZVAL(pad_value)
4632 	ZEND_PARSE_PARAMETERS_END();
4633 
4634 	if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4635 		zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4636 		RETURN_THROWS();
4637 	}
4638 
4639 	/* Do some initial calculations */
4640 	input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4641 	pad_size_abs = ZEND_ABS(pad_size);
4642 
4643 	if (input_size >= pad_size_abs) {
4644 		/* Copy the original array */
4645 		ZVAL_COPY(return_value, input);
4646 		return;
4647 	}
4648 
4649 	num_pads = pad_size_abs - input_size;
4650 	if (Z_REFCOUNTED_P(pad_value)) {
4651 		GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4652 	}
4653 
4654 	array_init_size(return_value, pad_size_abs);
4655 	if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4656 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4657 
4658 		if (pad_size < 0) {
4659 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4660 				for (i = 0; i < num_pads; i++) {
4661 					ZEND_HASH_FILL_ADD(pad_value);
4662 				}
4663 			} ZEND_HASH_FILL_END();
4664 		}
4665 
4666 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4667 			ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4668 				Z_TRY_ADDREF_P(value);
4669 				ZEND_HASH_FILL_ADD(value);
4670 			} ZEND_HASH_FOREACH_END();
4671 		} ZEND_HASH_FILL_END();
4672 
4673 		if (pad_size > 0) {
4674 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4675 				for (i = 0; i < num_pads; i++) {
4676 					ZEND_HASH_FILL_ADD(pad_value);
4677 				}
4678 			} ZEND_HASH_FILL_END();
4679 		}
4680 	} else {
4681 		if (pad_size < 0) {
4682 			for (i = 0; i < num_pads; i++) {
4683 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4684 			}
4685 		}
4686 
4687 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4688 			Z_TRY_ADDREF_P(value);
4689 			if (key) {
4690 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4691 			} else {
4692 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4693 			}
4694 		} ZEND_HASH_FOREACH_END();
4695 
4696 		if (pad_size > 0) {
4697 			for (i = 0; i < num_pads; i++) {
4698 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4699 			}
4700 		}
4701 	}
4702 }
4703 /* }}} */
4704 
4705 /* {{{ Return array with key <-> value flipped */
4706 PHP_FUNCTION(array_flip)
4707 {
4708 	zval *array, *entry, data;
4709 	zend_ulong num_idx;
4710 	zend_string *str_idx;
4711 
4712 	ZEND_PARSE_PARAMETERS_START(1, 1)
4713 		Z_PARAM_ARRAY(array)
4714 	ZEND_PARSE_PARAMETERS_END();
4715 
4716 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4717 
4718 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4719 		ZVAL_DEREF(entry);
4720 		if (Z_TYPE_P(entry) == IS_LONG) {
4721 			if (str_idx) {
4722 				ZVAL_STR_COPY(&data, str_idx);
4723 			} else {
4724 				ZVAL_LONG(&data, num_idx);
4725 			}
4726 			zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4727 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4728 			if (str_idx) {
4729 				ZVAL_STR_COPY(&data, str_idx);
4730 			} else {
4731 				ZVAL_LONG(&data, num_idx);
4732 			}
4733 			zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4734 		} else {
4735 			php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4736 		}
4737 	} ZEND_HASH_FOREACH_END();
4738 }
4739 /* }}} */
4740 
4741 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4742 PHP_FUNCTION(array_change_key_case)
4743 {
4744 	zval *array, *entry;
4745 	zend_string *string_key;
4746 	zend_string *new_key;
4747 	zend_ulong num_key;
4748 	zend_long change_to_upper=0;
4749 
4750 	ZEND_PARSE_PARAMETERS_START(1, 2)
4751 		Z_PARAM_ARRAY(array)
4752 		Z_PARAM_OPTIONAL
4753 		Z_PARAM_LONG(change_to_upper)
4754 	ZEND_PARSE_PARAMETERS_END();
4755 
4756 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4757 
4758 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4759 		if (!string_key) {
4760 			entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4761 		} else {
4762 			if (change_to_upper) {
4763 				new_key = zend_string_toupper(string_key);
4764 			} else {
4765 				new_key = zend_string_tolower(string_key);
4766 			}
4767 			entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4768 			zend_string_release_ex(new_key, 0);
4769 		}
4770 
4771 		zval_add_ref(entry);
4772 	} ZEND_HASH_FOREACH_END();
4773 }
4774 /* }}} */
4775 
4776 struct bucketindex {
4777 	Bucket b;
4778 	unsigned int i;
4779 };
4780 
4781 static void array_bucketindex_swap(void *p, void *q)
4782 {
4783 	struct bucketindex *f = (struct bucketindex *)p;
4784 	struct bucketindex *g = (struct bucketindex *)q;
4785 	struct bucketindex t;
4786 	t = *f;
4787 	*f = *g;
4788 	*g = t;
4789 }
4790 
4791 /* {{{ Removes duplicate values from array */
4792 PHP_FUNCTION(array_unique)
4793 {
4794 	zval *array;
4795 	Bucket *p;
4796 	zend_long sort_type = PHP_SORT_STRING;
4797 	bucket_compare_func_t cmp;
4798 	struct bucketindex *arTmp, *cmpdata, *lastkept;
4799 	uint32_t i, idx;
4800 
4801 	ZEND_PARSE_PARAMETERS_START(1, 2)
4802 		Z_PARAM_ARRAY(array)
4803 		Z_PARAM_OPTIONAL
4804 		Z_PARAM_LONG(sort_type)
4805 	ZEND_PARSE_PARAMETERS_END();
4806 
4807 	if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {	/* nothing to do */
4808 		ZVAL_COPY(return_value, array);
4809 		return;
4810 	}
4811 
4812 	if (sort_type == PHP_SORT_STRING) {
4813 		HashTable seen;
4814 		zend_long num_key;
4815 		zend_string *str_key;
4816 		zval *val;
4817 
4818 		zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4819 		array_init(return_value);
4820 
4821 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4822 			zval *retval;
4823 			if (Z_TYPE_P(val) == IS_STRING) {
4824 				retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4825 			} else {
4826 				zend_string *tmp_str_val;
4827 				zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4828 				retval = zend_hash_add_empty_element(&seen, str_val);
4829 				zend_tmp_string_release(tmp_str_val);
4830 			}
4831 
4832 			if (retval) {
4833 				/* First occurrence of the value */
4834 				if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4835 					ZVAL_DEREF(val);
4836 				}
4837 				Z_TRY_ADDREF_P(val);
4838 
4839 				if (str_key) {
4840 					zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4841 				} else {
4842 					zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4843 				}
4844 			}
4845 		} ZEND_HASH_FOREACH_END();
4846 
4847 		zend_hash_destroy(&seen);
4848 		return;
4849 	}
4850 
4851 	cmp = php_get_data_compare_func_unstable(sort_type, 0);
4852 
4853 	bool in_place = zend_may_modify_arg_in_place(array);
4854 	if (in_place) {
4855 		RETVAL_ARR(Z_ARRVAL_P(array));
4856 	} else {
4857 		RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4858 	}
4859 
4860 	/* create and sort array with pointers to the target_hash buckets */
4861 	arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4862 	if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4863 		zval *zv = Z_ARRVAL_P(array)->arPacked;
4864 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4865 			if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4866 			ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4867 			arTmp[i].b.h = idx;
4868 			arTmp[i].b.key = NULL;
4869 			arTmp[i].i = i;
4870 			i++;
4871 		}
4872 	} else {
4873 		p = Z_ARRVAL_P(array)->arData;
4874 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4875 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
4876 			arTmp[i].b = *p;
4877 			arTmp[i].i = i;
4878 			i++;
4879 		}
4880 	}
4881 	ZVAL_UNDEF(&arTmp[i].b.val);
4882 	zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4883 			(compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4884 	/* go through the sorted array and delete duplicates from the copy */
4885 	lastkept = arTmp;
4886 	for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4887 		if (cmp(&lastkept->b, &cmpdata->b)) {
4888 			lastkept = cmpdata;
4889 		} else {
4890 			if (lastkept->i > cmpdata->i) {
4891 				p = &lastkept->b;
4892 				lastkept = cmpdata;
4893 			} else {
4894 				p = &cmpdata->b;
4895 			}
4896 			if (p->key == NULL) {
4897 				zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4898 			} else {
4899 				zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4900 			}
4901 		}
4902 	}
4903 	pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4904 
4905 	if (in_place) {
4906 		Z_ADDREF_P(return_value);
4907 	}
4908 }
4909 /* }}} */
4910 
4911 static int zval_compare(zval *first, zval *second) /* {{{ */
4912 {
4913 	return string_compare_function(first, second);
4914 }
4915 /* }}} */
4916 
4917 static int zval_user_compare(zval *a, zval *b) /* {{{ */
4918 {
4919 	zval args[2];
4920 	zval retval;
4921 
4922 	ZVAL_COPY_VALUE(&args[0], a);
4923 	ZVAL_COPY_VALUE(&args[1], b);
4924 
4925 	BG(user_compare_fci).param_count = 2;
4926 	BG(user_compare_fci).params = args;
4927 	BG(user_compare_fci).retval = &retval;
4928 
4929 	if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4930 		zend_long ret = zval_get_long(&retval);
4931 		zval_ptr_dtor(&retval);
4932 		return ZEND_NORMALIZE_BOOL(ret);
4933 	} else {
4934 		return 0;
4935 	}
4936 }
4937 /* }}} */
4938 
4939 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4940 {
4941 	uint32_t argc, i;
4942 	zval *args;
4943 	int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4944 	bool ok;
4945 	zval *val, *data;
4946 	char *param_spec;
4947 	zend_string *key;
4948 	zend_ulong h;
4949 
4950 	/* Get the argument count */
4951 	argc = ZEND_NUM_ARGS();
4952 	if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4953 		/* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
4954 		param_spec = "+f";
4955 		intersect_data_compare_func = zval_user_compare;
4956 	} else {
4957 		/* 	INTERSECT_COMP_DATA_NONE - array_intersect_key()
4958 			INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
4959 		param_spec = "+";
4960 
4961 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4962 			intersect_data_compare_func = zval_compare;
4963 		}
4964 	}
4965 
4966 	if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4967 		RETURN_THROWS();
4968 	}
4969 
4970 	for (i = 0; i < argc; i++) {
4971 		if (Z_TYPE(args[i]) != IS_ARRAY) {
4972 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
4973 			RETURN_THROWS();
4974 		}
4975 	}
4976 
4977 	array_init(return_value);
4978 
4979 	/* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
4980 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4981 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4982 			val = Z_REFVAL_P(val);
4983 		}
4984 		if (key == NULL) {
4985 			ok = 1;
4986 			for (i = 1; i < argc; i++) {
4987 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
4988 					(intersect_data_compare_func &&
4989 					intersect_data_compare_func(val, data) != 0)
4990 				) {
4991 					ok = 0;
4992 					break;
4993 				}
4994 			}
4995 			if (ok) {
4996 				Z_TRY_ADDREF_P(val);
4997 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4998 			}
4999 		} else {
5000 			ok = 1;
5001 			for (i = 1; i < argc; i++) {
5002 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5003 					(intersect_data_compare_func &&
5004 					intersect_data_compare_func(val, data) != 0)
5005 				) {
5006 					ok = 0;
5007 					break;
5008 				}
5009 			}
5010 			if (ok) {
5011 				Z_TRY_ADDREF_P(val);
5012 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5013 			}
5014 		}
5015 	} ZEND_HASH_FOREACH_END();
5016 }
5017 /* }}} */
5018 
5019 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5020 {
5021 	zval *args = NULL;
5022 	HashTable *hash;
5023 	uint32_t arr_argc, i;
5024 	int c = 0;
5025 	uint32_t idx;
5026 	Bucket **lists, *list, **ptrs, *p;
5027 	char *param_spec;
5028 	zend_fcall_info fci1, fci2;
5029 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5030 	zend_fcall_info *fci_key = NULL, *fci_data;
5031 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5032 	PHP_ARRAY_CMP_FUNC_VARS;
5033 	bool in_place = false;
5034 
5035 	bucket_compare_func_t intersect_key_compare_func;
5036 	bucket_compare_func_t intersect_data_compare_func;
5037 
5038 	if (behavior == INTERSECT_NORMAL) {
5039 		intersect_key_compare_func = php_array_key_compare_string;
5040 
5041 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5042 			/* array_intersect() */
5043 			param_spec = "+";
5044 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5045 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5046 			/* array_uintersect() */
5047 			param_spec = "+f";
5048 			intersect_data_compare_func = php_array_user_compare_unstable;
5049 		} else {
5050 			ZEND_ASSERT(0 && "Invalid data_compare_type");
5051 			return;
5052 		}
5053 
5054 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5055 			RETURN_THROWS();
5056 		}
5057 		fci_data = &fci1;
5058 		fci_data_cache = &fci1_cache;
5059 
5060 	} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5061 		/* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5062 		 * no comparison of the data is done (part of INTERSECT_ASSOC) */
5063 
5064 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5065 			/* array_intersect_assoc() or array_intersect_key() */
5066 			param_spec = "+";
5067 			intersect_key_compare_func = php_array_key_compare_string_unstable;
5068 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5069 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5070 			/* array_uintersect_assoc() */
5071 			param_spec = "+f";
5072 			intersect_key_compare_func = php_array_key_compare_string_unstable;
5073 			intersect_data_compare_func = php_array_user_compare_unstable;
5074 			fci_data = &fci1;
5075 			fci_data_cache = &fci1_cache;
5076 		} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5077 			/* array_intersect_uassoc() or array_intersect_ukey() */
5078 			param_spec = "+f";
5079 			intersect_key_compare_func = php_array_user_key_compare_unstable;
5080 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5081 			fci_key = &fci1;
5082 			fci_key_cache = &fci1_cache;
5083 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5084 			/* array_uintersect_uassoc() */
5085 			param_spec = "+ff";
5086 			intersect_key_compare_func = php_array_user_key_compare_unstable;
5087 			intersect_data_compare_func = php_array_user_compare_unstable;
5088 			fci_data = &fci1;
5089 			fci_data_cache = &fci1_cache;
5090 			fci_key = &fci2;
5091 			fci_key_cache = &fci2_cache;
5092 		} else {
5093 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5094 			return;
5095 		}
5096 
5097 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5098 			RETURN_THROWS();
5099 		}
5100 
5101 	} else {
5102 		ZEND_ASSERT(0 && "Invalid behavior");
5103 		return;
5104 	}
5105 
5106 	PHP_ARRAY_CMP_FUNC_BACKUP();
5107 
5108 	/* for each argument, create and sort list with pointers to the hash buckets */
5109 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5110 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5111 
5112 	if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5113 		BG(user_compare_fci) = *fci_data;
5114 		BG(user_compare_fci_cache) = *fci_data_cache;
5115 	} else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5116 		BG(user_compare_fci) = *fci_key;
5117 		BG(user_compare_fci_cache) = *fci_key_cache;
5118 	}
5119 
5120 	for (i = 0; i < arr_argc; i++) {
5121 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5122 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5123 			arr_argc = i; /* only free up to i - 1 */
5124 			goto out;
5125 		}
5126 		hash = Z_ARRVAL(args[i]);
5127 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5128 		lists[i] = list;
5129 		ptrs[i] = list;
5130 		if (HT_IS_PACKED(hash)) {
5131 			zval *zv = hash->arPacked;
5132 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5133 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5134 				ZVAL_COPY_VALUE(&list->val, zv);
5135 				list->h = idx;
5136 				list->key = NULL;
5137 				list++;
5138 			}
5139 		} else {
5140 			p = hash->arData;
5141 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5142 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5143 				*list++ = *p;
5144 			}
5145 		}
5146 		ZVAL_UNDEF(&list->val);
5147 		if (hash->nNumOfElements > 1) {
5148 			if (behavior == INTERSECT_NORMAL) {
5149 				zend_sort((void *) lists[i], hash->nNumOfElements,
5150 						sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5151 						(swap_func_t)zend_hash_bucket_swap);
5152 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5153 				zend_sort((void *) lists[i], hash->nNumOfElements,
5154 						sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5155 						(swap_func_t)zend_hash_bucket_swap);
5156 			}
5157 		}
5158 	}
5159 
5160 	/* copy the argument array if necessary */
5161 	in_place = zend_may_modify_arg_in_place(&args[0]);
5162 	if (in_place) {
5163 		RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5164 	} else {
5165 		RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5166 	}
5167 
5168 	/* go through the lists and look for common values */
5169 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5170 		if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5171 			&& key_compare_type == INTERSECT_COMP_KEY_USER) {
5172 			BG(user_compare_fci) = *fci_key;
5173 			BG(user_compare_fci_cache) = *fci_key_cache;
5174 		}
5175 
5176 		for (i = 1; i < arr_argc; i++) {
5177 			if (behavior & INTERSECT_NORMAL) {
5178 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5179 					ptrs[i]++;
5180 				}
5181 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5182 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5183 					ptrs[i]++;
5184 				}
5185 				if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5186 					/* this means that ptrs[i] is not NULL so we can compare
5187 					 * and "c==0" is from last operation
5188 					 * in this branch of code we enter only when INTERSECT_ASSOC
5189 					 * since when we have INTERSECT_KEY compare of data is not wanted. */
5190 					if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5191 						BG(user_compare_fci) = *fci_data;
5192 						BG(user_compare_fci_cache) = *fci_data_cache;
5193 					}
5194 					if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5195 						c = 1;
5196 						if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5197 							BG(user_compare_fci) = *fci_key;
5198 							BG(user_compare_fci_cache) = *fci_key_cache;
5199 							/* When KEY_USER, the last parameter is always the callback */
5200 						}
5201 						/* we are going to the break */
5202 					} else {
5203 						/* continue looping */
5204 					}
5205 				}
5206 			}
5207 			if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5208 				/* delete any values corresponding to remains of ptrs[0] */
5209 				/* and exit because they do not present in at least one of */
5210 				/* the other arguments */
5211 				for (;;) {
5212 					p = ptrs[0]++;
5213 					if (Z_TYPE(p->val) == IS_UNDEF) {
5214 						goto out;
5215 					}
5216 					if (p->key == NULL) {
5217 						zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5218 					} else {
5219 						zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5220 					}
5221 				}
5222 			}
5223 			if (c) /* here we get if not all are equal */
5224 				break;
5225 			ptrs[i]++;
5226 		}
5227 		if (c) {
5228 			/* Value of ptrs[0] not in all arguments, delete all entries */
5229 			/* with value < value of ptrs[i] */
5230 			for (;;) {
5231 				p = ptrs[0];
5232 				if (p->key == NULL) {
5233 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5234 				} else {
5235 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5236 				}
5237 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5238 					goto out;
5239 				}
5240 				if (behavior == INTERSECT_NORMAL) {
5241 					if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5242 						break;
5243 					}
5244 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5245 					/* no need of looping because indexes are unique */
5246 					break;
5247 				}
5248 			}
5249 		} else {
5250 			/* ptrs[0] is present in all the arguments */
5251 			/* Skip all entries with same value as ptrs[0] */
5252 			for (;;) {
5253 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5254 					goto out;
5255 				}
5256 				if (behavior == INTERSECT_NORMAL) {
5257 					if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5258 						break;
5259 					}
5260 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5261 					/* no need of looping because indexes are unique */
5262 					break;
5263 				}
5264 			}
5265 		}
5266 	}
5267 out:
5268 	for (i = 0; i < arr_argc; i++) {
5269 		hash = Z_ARRVAL(args[i]);
5270 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5271 	}
5272 
5273 	PHP_ARRAY_CMP_FUNC_RESTORE();
5274 
5275 	efree(ptrs);
5276 	efree(lists);
5277 
5278 	if (in_place) {
5279 		Z_ADDREF_P(return_value);
5280 	}
5281 }
5282 /* }}} */
5283 
5284 /* {{{ 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. */
5285 PHP_FUNCTION(array_intersect_key)
5286 {
5287 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5288 }
5289 /* }}} */
5290 
5291 /* {{{ 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. */
5292 PHP_FUNCTION(array_intersect_ukey)
5293 {
5294 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5295 }
5296 /* }}} */
5297 
5298 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5299 PHP_FUNCTION(array_intersect)
5300 {
5301 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5302 }
5303 /* }}} */
5304 
5305 /* {{{ 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. */
5306 PHP_FUNCTION(array_uintersect)
5307 {
5308 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5309 }
5310 /* }}} */
5311 
5312 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5313 PHP_FUNCTION(array_intersect_assoc)
5314 {
5315 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5316 }
5317 /* }}} */
5318 
5319 /* {{{ 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. */
5320 PHP_FUNCTION(array_intersect_uassoc)
5321 {
5322 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5323 }
5324 /* }}} */
5325 
5326 /* {{{ 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. */
5327 PHP_FUNCTION(array_uintersect_assoc)
5328 {
5329 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5330 }
5331 /* }}} */
5332 
5333 /* {{{ 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. */
5334 PHP_FUNCTION(array_uintersect_uassoc)
5335 {
5336 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5337 }
5338 /* }}} */
5339 
5340 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5341 {
5342 	uint32_t argc, i;
5343 	zval *args;
5344 	int (*diff_data_compare_func)(zval *, zval *) = NULL;
5345 	bool ok;
5346 	zval *val, *data;
5347 	zend_string *key;
5348 	zend_ulong h;
5349 
5350 	/* Get the argument count */
5351 	argc = ZEND_NUM_ARGS();
5352 	if (data_compare_type == DIFF_COMP_DATA_USER) {
5353 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5354 			RETURN_THROWS();
5355 		}
5356 		diff_data_compare_func = zval_user_compare;
5357 	} else {
5358 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5359 			RETURN_THROWS();
5360 		}
5361 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5362 			diff_data_compare_func = zval_compare;
5363 		}
5364 	}
5365 
5366 	for (i = 0; i < argc; i++) {
5367 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5368 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5369 			RETURN_THROWS();
5370 		}
5371 	}
5372 
5373 	array_init(return_value);
5374 
5375 	/* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5376 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5377 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5378 			val = Z_REFVAL_P(val);
5379 		}
5380 		if (key == NULL) {
5381 			ok = 1;
5382 			for (i = 1; i < argc; i++) {
5383 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5384 					(!diff_data_compare_func ||
5385 					diff_data_compare_func(val, data) == 0)
5386 				) {
5387 					ok = 0;
5388 					break;
5389 				}
5390 			}
5391 			if (ok) {
5392 				Z_TRY_ADDREF_P(val);
5393 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5394 			}
5395 		} else {
5396 			ok = 1;
5397 			for (i = 1; i < argc; i++) {
5398 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5399 					(!diff_data_compare_func ||
5400 					diff_data_compare_func(val, data) == 0)
5401 				) {
5402 					ok = 0;
5403 					break;
5404 				}
5405 			}
5406 			if (ok) {
5407 				Z_TRY_ADDREF_P(val);
5408 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5409 			}
5410 		}
5411 	} ZEND_HASH_FOREACH_END();
5412 }
5413 /* }}} */
5414 
5415 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5416 {
5417 	zval *args = NULL;
5418 	HashTable *hash;
5419 	uint32_t arr_argc, i;
5420 	int c;
5421 	uint32_t idx;
5422 	Bucket **lists, *list, **ptrs, *p;
5423 	char *param_spec;
5424 	zend_fcall_info fci1, fci2;
5425 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5426 	zend_fcall_info *fci_key = NULL, *fci_data;
5427 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5428 	PHP_ARRAY_CMP_FUNC_VARS;
5429 
5430 	bucket_compare_func_t diff_key_compare_func;
5431 	bucket_compare_func_t diff_data_compare_func;
5432 
5433 	if (behavior == DIFF_NORMAL) {
5434 		diff_key_compare_func = php_array_key_compare_string_unstable;
5435 
5436 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5437 			/* array_diff */
5438 			param_spec = "+";
5439 			diff_data_compare_func = php_array_data_compare_string_unstable;
5440 		} else if (data_compare_type == DIFF_COMP_DATA_USER) {
5441 			/* array_udiff */
5442 			param_spec = "+f";
5443 			diff_data_compare_func = php_array_user_compare_unstable;
5444 		} else {
5445 			ZEND_ASSERT(0 && "Invalid data_compare_type");
5446 			return;
5447 		}
5448 
5449 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5450 			RETURN_THROWS();
5451 		}
5452 		fci_data = &fci1;
5453 		fci_data_cache = &fci1_cache;
5454 
5455 	} else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5456 		/* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5457 		 * no comparison of the data is done (part of DIFF_ASSOC) */
5458 
5459 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5460 			/* array_diff_assoc() or array_diff_key() */
5461 			param_spec = "+";
5462 			diff_key_compare_func = php_array_key_compare_string_unstable;
5463 			diff_data_compare_func = php_array_data_compare_string_unstable;
5464 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5465 			/* array_udiff_assoc() */
5466 			param_spec = "+f";
5467 			diff_key_compare_func = php_array_key_compare_string_unstable;
5468 			diff_data_compare_func = php_array_user_compare_unstable;
5469 			fci_data = &fci1;
5470 			fci_data_cache = &fci1_cache;
5471 		} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5472 			/* array_diff_uassoc() or array_diff_ukey() */
5473 			param_spec = "+f";
5474 			diff_key_compare_func = php_array_user_key_compare_unstable;
5475 			diff_data_compare_func = php_array_data_compare_string_unstable;
5476 			fci_key = &fci1;
5477 			fci_key_cache = &fci1_cache;
5478 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5479 			/* array_udiff_uassoc() */
5480 			param_spec = "+ff";
5481 			diff_key_compare_func = php_array_user_key_compare_unstable;
5482 			diff_data_compare_func = php_array_user_compare_unstable;
5483 			fci_data = &fci1;
5484 			fci_data_cache = &fci1_cache;
5485 			fci_key = &fci2;
5486 			fci_key_cache = &fci2_cache;
5487 		} else {
5488 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5489 			return;
5490 		}
5491 
5492 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5493 			RETURN_THROWS();
5494 		}
5495 
5496 	} else {
5497 		ZEND_ASSERT(0 && "Invalid behavior");
5498 		return;
5499 	}
5500 
5501 	PHP_ARRAY_CMP_FUNC_BACKUP();
5502 
5503 	/* for each argument, create and sort list with pointers to the hash buckets */
5504 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5505 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5506 
5507 	if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5508 		BG(user_compare_fci) = *fci_data;
5509 		BG(user_compare_fci_cache) = *fci_data_cache;
5510 	} else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5511 		BG(user_compare_fci) = *fci_key;
5512 		BG(user_compare_fci_cache) = *fci_key_cache;
5513 	}
5514 
5515 	for (i = 0; i < arr_argc; i++) {
5516 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5517 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5518 			arr_argc = i; /* only free up to i - 1 */
5519 			goto out;
5520 		}
5521 		hash = Z_ARRVAL(args[i]);
5522 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5523 		lists[i] = list;
5524 		ptrs[i] = list;
5525 		if (HT_IS_PACKED(hash)) {
5526 			zval *zv = hash->arPacked;
5527 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5528 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5529 				ZVAL_COPY_VALUE(&list->val, zv);
5530 				list->h = idx;
5531 				list->key = NULL;
5532 				list++;
5533 			}
5534 		} else {
5535 			p = hash->arData;
5536 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5537 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5538 				*list++ = *p;
5539 			}
5540 		}
5541 		ZVAL_UNDEF(&list->val);
5542 		if (hash->nNumOfElements > 1) {
5543 			if (behavior == DIFF_NORMAL) {
5544 				zend_sort((void *) lists[i], hash->nNumOfElements,
5545 						sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5546 						(swap_func_t)zend_hash_bucket_swap);
5547 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5548 				zend_sort((void *) lists[i], hash->nNumOfElements,
5549 						sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5550 						(swap_func_t)zend_hash_bucket_swap);
5551 			}
5552 		}
5553 	}
5554 
5555 	/* copy the argument array */
5556 	RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5557 
5558 	/* go through the lists and look for values of ptr[0] that are not in the others */
5559 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5560 		if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5561 			&&
5562 			key_compare_type == DIFF_COMP_KEY_USER
5563 		) {
5564 			BG(user_compare_fci) = *fci_key;
5565 			BG(user_compare_fci_cache) = *fci_key_cache;
5566 		}
5567 		c = 1;
5568 		for (i = 1; i < arr_argc; i++) {
5569 			Bucket *ptr = ptrs[i];
5570 			if (behavior == DIFF_NORMAL) {
5571 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5572 					ptrs[i]++;
5573 				}
5574 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5575 				while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5576 					ptr++;
5577 				}
5578 			}
5579 			if (!c) {
5580 				if (behavior == DIFF_NORMAL) {
5581 					if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5582 						ptrs[i]++;
5583 					}
5584 					break;
5585 				} else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
5586 					/* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5587 					 * data comparison is not needed - skipped. */
5588 					if (Z_TYPE(ptr->val) != IS_UNDEF) {
5589 						if (data_compare_type == DIFF_COMP_DATA_USER) {
5590 							BG(user_compare_fci) = *fci_data;
5591 							BG(user_compare_fci_cache) = *fci_data_cache;
5592 						}
5593 						if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5594 							/* the data is not the same */
5595 							c = -1;
5596 							if (key_compare_type == DIFF_COMP_KEY_USER) {
5597 								BG(user_compare_fci) = *fci_key;
5598 								BG(user_compare_fci_cache) = *fci_key_cache;
5599 							}
5600 						} else {
5601 							break;
5602 							/* we have found the element in other arrays thus we don't want it
5603 							 * in the return_value -> delete from there */
5604 						}
5605 					}
5606 				} else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5607 					/* the behavior here differs from INTERSECT_KEY in php_intersect
5608 					 * since in the "diff" case we have to remove the entry from
5609 					 * return_value while when doing intersection the entry must not
5610 					 * be deleted. */
5611 					break; /* remove the key */
5612 				}
5613 			}
5614 		}
5615 		if (!c) {
5616 			/* ptrs[0] in one of the other arguments */
5617 			/* delete all entries with value as ptrs[0] */
5618 			for (;;) {
5619 				p = ptrs[0];
5620 				if (p->key == NULL) {
5621 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5622 				} else {
5623 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5624 				}
5625 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5626 					goto out;
5627 				}
5628 				if (behavior == DIFF_NORMAL) {
5629 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5630 						break;
5631 					}
5632 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5633 					/* in this case no array_key_compare is needed */
5634 					break;
5635 				}
5636 			}
5637 		} else {
5638 			/* ptrs[0] in none of the other arguments */
5639 			/* skip all entries with value as ptrs[0] */
5640 			for (;;) {
5641 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5642 					goto out;
5643 				}
5644 				if (behavior == DIFF_NORMAL) {
5645 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5646 						break;
5647 					}
5648 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5649 					/* in this case no array_key_compare is needed */
5650 					break;
5651 				}
5652 			}
5653 		}
5654 	}
5655 out:
5656 	for (i = 0; i < arr_argc; i++) {
5657 		hash = Z_ARRVAL(args[i]);
5658 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5659 	}
5660 
5661 	PHP_ARRAY_CMP_FUNC_RESTORE();
5662 
5663 	efree(ptrs);
5664 	efree(lists);
5665 }
5666 /* }}} */
5667 
5668 /* {{{ 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. */
5669 PHP_FUNCTION(array_diff_key)
5670 {
5671 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5672 }
5673 /* }}} */
5674 
5675 /* {{{ 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. */
5676 PHP_FUNCTION(array_diff_ukey)
5677 {
5678 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5679 }
5680 /* }}} */
5681 
5682 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5683 PHP_FUNCTION(array_diff)
5684 {
5685 	zval *args;
5686 	uint32_t argc, i;
5687 	uint32_t num;
5688 	HashTable exclude;
5689 	zval *value;
5690 	zend_string *str, *tmp_str, *key;
5691 	zend_long idx;
5692 	zval dummy;
5693 
5694 	ZEND_PARSE_PARAMETERS_START(1, -1)
5695 		Z_PARAM_VARIADIC('+', args, argc)
5696 	ZEND_PARSE_PARAMETERS_END();
5697 
5698 	if (Z_TYPE(args[0]) != IS_ARRAY) {
5699 		zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5700 		RETURN_THROWS();
5701 	}
5702 
5703 	num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5704 	if (num == 0) {
5705 		for (i = 1; i < argc; i++) {
5706 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5707 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5708 				RETURN_THROWS();
5709 			}
5710 		}
5711 		RETURN_EMPTY_ARRAY();
5712 	} else if (num == 1) {
5713 		int found = 0;
5714 		zend_string *search_str, *tmp_search_str;
5715 
5716 		value = NULL;
5717 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5718 			break;
5719 		} ZEND_HASH_FOREACH_END();
5720 
5721 		if (!value) {
5722 			for (i = 1; i < argc; i++) {
5723 				if (Z_TYPE(args[i]) != IS_ARRAY) {
5724 					zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5725 					RETURN_THROWS();
5726 				}
5727 			}
5728 			RETURN_EMPTY_ARRAY();
5729 		}
5730 
5731 		search_str = zval_get_tmp_string(value, &tmp_search_str);
5732 
5733 		for (i = 1; i < argc; i++) {
5734 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5735 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5736 				RETURN_THROWS();
5737 			}
5738 			if (!found) {
5739 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5740 					str = zval_get_tmp_string(value, &tmp_str);
5741 					if (zend_string_equals(search_str, str)) {
5742 						zend_tmp_string_release(tmp_str);
5743 						found = 1;
5744 						break;
5745 					}
5746 					zend_tmp_string_release(tmp_str);
5747 				} ZEND_HASH_FOREACH_END();
5748 			}
5749 		}
5750 
5751 		zend_tmp_string_release(tmp_search_str);
5752 
5753 		if (found) {
5754 			RETVAL_EMPTY_ARRAY();
5755 		} else {
5756 			ZVAL_COPY(return_value, &args[0]);
5757 		}
5758 		return;
5759 	}
5760 
5761 	/* count number of elements */
5762 	num = 0;
5763 	for (i = 1; i < argc; i++) {
5764 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5765 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5766 			RETURN_THROWS();
5767 		}
5768 		num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5769 	}
5770 
5771 	if (num == 0) {
5772 		ZVAL_COPY(return_value, &args[0]);
5773 		return;
5774 	}
5775 
5776 	ZVAL_NULL(&dummy);
5777 	/* create exclude map */
5778 	zend_hash_init(&exclude, num, NULL, NULL, 0);
5779 	for (i = 1; i < argc; i++) {
5780 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5781 			str = zval_get_tmp_string(value, &tmp_str);
5782 			zend_hash_add(&exclude, str, &dummy);
5783 			zend_tmp_string_release(tmp_str);
5784 		} ZEND_HASH_FOREACH_END();
5785 	}
5786 
5787 	/* copy all elements of first array that are not in exclude set */
5788 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5789 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5790 		str = zval_get_tmp_string(value, &tmp_str);
5791 		if (!zend_hash_exists(&exclude, str)) {
5792 			if (key) {
5793 				value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5794 			} else {
5795 				value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5796 			}
5797 			zval_add_ref(value);
5798 		}
5799 		zend_tmp_string_release(tmp_str);
5800 	} ZEND_HASH_FOREACH_END();
5801 
5802 	zend_hash_destroy(&exclude);
5803 }
5804 /* }}} */
5805 
5806 /* {{{ 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. */
5807 PHP_FUNCTION(array_udiff)
5808 {
5809 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5810 }
5811 /* }}} */
5812 
5813 /* {{{ 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 */
5814 PHP_FUNCTION(array_diff_assoc)
5815 {
5816 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5817 }
5818 /* }}} */
5819 
5820 /* {{{ 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. */
5821 PHP_FUNCTION(array_diff_uassoc)
5822 {
5823 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5824 }
5825 /* }}} */
5826 
5827 /* {{{ 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. */
5828 PHP_FUNCTION(array_udiff_assoc)
5829 {
5830 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5831 }
5832 /* }}} */
5833 
5834 /* {{{ 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. */
5835 PHP_FUNCTION(array_udiff_uassoc)
5836 {
5837 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5838 }
5839 /* }}} */
5840 
5841 #define MULTISORT_ORDER	0
5842 #define MULTISORT_TYPE	1
5843 #define MULTISORT_LAST	2
5844 
5845 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5846 {
5847 	Bucket *ab = *(Bucket **)a;
5848 	Bucket *bb = *(Bucket **)b;
5849 	int r;
5850 	zend_long result;
5851 
5852 	r = 0;
5853 	do {
5854 		result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5855 		if (result != 0) {
5856 			return result > 0 ? 1 : -1;
5857 		}
5858 		r++;
5859 	} while (Z_TYPE(ab[r].val) != IS_UNDEF);
5860 
5861 	return stable_sort_fallback(&ab[r], &bb[r]);
5862 }
5863 /* }}} */
5864 
5865 #define MULTISORT_ABORT				\
5866 	efree(func);	\
5867 	efree(arrays);					\
5868 	return;
5869 
5870 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5871 	Bucket *t;
5872 	Bucket **f = (Bucket **)p;
5873 	Bucket **g = (Bucket **)q;
5874 
5875 	t = *f;
5876 	*f = *g;
5877 	*g = t;
5878 }
5879 /* }}} */
5880 
5881 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5882 PHP_FUNCTION(array_multisort)
5883 {
5884 	zval*			args;
5885 	zval**			arrays;
5886 	Bucket**		indirect;
5887 	uint32_t		idx;
5888 	HashTable*		hash;
5889 	uint32_t		argc;
5890 	uint32_t		array_size;
5891 	uint32_t		num_arrays = 0;
5892 	int				parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
5893 	int				sort_order = PHP_SORT_ASC;
5894 	int				sort_type  = PHP_SORT_REGULAR;
5895 	uint32_t		i, k, n;
5896 	bucket_compare_func_t *func;
5897 
5898 	ZEND_PARSE_PARAMETERS_START(1, -1)
5899 		Z_PARAM_VARIADIC('+', args, argc)
5900 	ZEND_PARSE_PARAMETERS_END();
5901 
5902 	/* Allocate space for storing pointers to input arrays and sort flags. */
5903 	arrays = (zval **)ecalloc(argc, sizeof(zval *));
5904 	for (i = 0; i < MULTISORT_LAST; i++) {
5905 		parse_state[i] = 0;
5906 	}
5907 	func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
5908 
5909 	/* Here we go through the input arguments and parse them. Each one can
5910 	 * be either an array or a sort flag which follows an array. If not
5911 	 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5912 	 * accordingly. There can't be two sort flags of the same type after an
5913 	 * array, and the very first argument has to be an array. */
5914 	for (i = 0; i < argc; i++) {
5915 		zval *arg = &args[i];
5916 
5917 		ZVAL_DEREF(arg);
5918 		if (Z_TYPE_P(arg) == IS_ARRAY) {
5919 			SEPARATE_ARRAY(arg);
5920 			/* We see the next array, so we update the sort flags of
5921 			 * the previous array and reset the sort flags. */
5922 			if (i > 0) {
5923 				ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5924 				sort_order = PHP_SORT_ASC;
5925 				sort_type = PHP_SORT_REGULAR;
5926 			}
5927 			arrays[num_arrays++] = arg;
5928 
5929 			/* Next one may be an array or a list of sort flags. */
5930 			for (k = 0; k < MULTISORT_LAST; k++) {
5931 				parse_state[k] = 1;
5932 			}
5933 		} else if (Z_TYPE_P(arg) == IS_LONG) {
5934 			switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5935 				case PHP_SORT_ASC:
5936 				case PHP_SORT_DESC:
5937 					/* flag allowed here */
5938 					if (parse_state[MULTISORT_ORDER] == 1) {
5939 						/* Save the flag and make sure then next arg is not the current flag. */
5940 						sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5941 						parse_state[MULTISORT_ORDER] = 0;
5942 					} else {
5943 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5944 						MULTISORT_ABORT;
5945 					}
5946 					break;
5947 
5948 				case PHP_SORT_REGULAR:
5949 				case PHP_SORT_NUMERIC:
5950 				case PHP_SORT_STRING:
5951 				case PHP_SORT_NATURAL:
5952 				case PHP_SORT_LOCALE_STRING:
5953 					/* flag allowed here */
5954 					if (parse_state[MULTISORT_TYPE] == 1) {
5955 						/* Save the flag and make sure then next arg is not the current flag. */
5956 						sort_type = (int)Z_LVAL_P(arg);
5957 						parse_state[MULTISORT_TYPE] = 0;
5958 					} else {
5959 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5960 						MULTISORT_ABORT;
5961 					}
5962 					break;
5963 
5964 				default:
5965 					zend_argument_value_error(i + 1, "must be a valid sort flag");
5966 					MULTISORT_ABORT;
5967 					break;
5968 
5969 			}
5970 		} else {
5971 			zend_argument_type_error(i + 1, "must be an array or a sort flag");
5972 			MULTISORT_ABORT;
5973 		}
5974 	}
5975 	/* Take care of the last array sort flags. */
5976 	ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5977 
5978 	/* Make sure the arrays are of the same size. */
5979 	array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
5980 	for (i = 1; i < num_arrays; i++) {
5981 		if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
5982 			zend_value_error("Array sizes are inconsistent");
5983 			MULTISORT_ABORT;
5984 		}
5985 	}
5986 
5987 	/* If all arrays are empty we don't need to do anything. */
5988 	if (array_size < 1) {
5989 		efree(func);
5990 		efree(arrays);
5991 		RETURN_TRUE;
5992 	}
5993 
5994 	/* Create the indirection array. This array is of size MxN, where
5995 	 * M is the number of entries in each input array and N is the number
5996 	 * of the input arrays + 1. The last column is UNDEF to indicate the end
5997 	 * of the row. It also stores the original position for stable sorting. */
5998 	indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
5999 	/* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6000 	Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6001 	for (i = 0; i < array_size; i++) {
6002 		indirect[i] = indirects + (i * (num_arrays + 1));
6003 	}
6004 	for (i = 0; i < num_arrays; i++) {
6005 		k = 0;
6006 		if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6007 			zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6008 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6009 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6010 				ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6011 				indirect[k][i].h = idx;
6012 				indirect[k][i].key = NULL;
6013 				k++;
6014 			}
6015 		} else {
6016 			Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6017 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6018 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
6019 				indirect[k][i] = *p;
6020 				k++;
6021 			}
6022 		}
6023 	}
6024 	for (k = 0; k < array_size; k++) {
6025 		ZVAL_UNDEF(&indirect[k][num_arrays].val);
6026 		Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6027 	}
6028 
6029 	/* Do the actual sort magic - bada-bim, bada-boom. */
6030 	zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6031 	if (EG(exception)) {
6032 		goto clean_up;
6033 	}
6034 
6035 	/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6036 	for (i = 0; i < num_arrays; i++) {
6037 		hash = Z_ARRVAL_P(arrays[i]);
6038 		hash->nNumUsed = array_size;
6039 		hash->nNextFreeElement = array_size;
6040 		hash->nInternalPointer = 0;
6041 		if (HT_IS_PACKED(hash)) {
6042 			for (k = 0; k < array_size; k++) {
6043 				ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6044 			}
6045 		} else {
6046 			bool repack = true;
6047 
6048 			for (n = 0, k = 0; k < array_size; k++) {
6049 				hash->arData[k] = indirect[k][i];
6050 				if (hash->arData[k].key == NULL) {
6051 					hash->arData[k].h = n++;
6052 				} else {
6053 					repack = false;
6054 				}
6055 			}
6056 			if (repack) {
6057 				zend_hash_to_packed(hash);
6058 			} else {
6059 				zend_hash_rehash(hash);
6060 			}
6061 		}
6062 	}
6063 	RETVAL_TRUE;
6064 
6065 clean_up:
6066 	efree(indirects);
6067 	efree(indirect);
6068 	efree(func);
6069 	efree(arrays);
6070 }
6071 /* }}} */
6072 
6073 /* {{{ php_array_pick_keys */
6074 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)
6075 {
6076 	HashTable *ht = Z_ARRVAL_P(input);
6077 	uint32_t num_avail = zend_hash_num_elements(ht);
6078 	zend_long i, randval;
6079 	zend_string *string_key;
6080 	zend_ulong num_key;
6081 	zval *zv;
6082 	Bucket *b;
6083 	zend_bitset bitset;
6084 	int negative_bitset = 0;
6085 	uint32_t bitset_len;
6086 	ALLOCA_FLAG(use_heap);
6087 
6088 	if (num_avail == 0) {
6089 		if (!silent) {
6090 			zend_argument_value_error(1, "cannot be empty");
6091 		}
6092 		return false;
6093 	}
6094 
6095 	if (num_req == 1) {
6096 		if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6097 			/* If less than 1/2 of elements are used, don't sample. Instead search for a
6098 			 * specific offset using linear scan. */
6099 			i = 0;
6100 			randval = algo->range(status, 0, num_avail - 1);
6101 			if (EG(exception)) {
6102 				return false;
6103 			}
6104 			ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6105 				if (i == randval) {
6106 					if (string_key) {
6107 						ZVAL_STR_COPY(retval, string_key);
6108 					} else {
6109 						ZVAL_LONG(retval, num_key);
6110 					}
6111 					return true;
6112 				}
6113 				i++;
6114 			} ZEND_HASH_FOREACH_END();
6115 		}
6116 
6117 		/* Sample random buckets until we hit one that is not empty.
6118 		 * The worst case probability of hitting an empty element is 1-1/2. The worst case
6119 		 * probability of hitting N empty elements in a row is (1-1/2)**N.
6120 		 * For N=10 this becomes smaller than 0.1%. */
6121 		if (HT_IS_PACKED(ht)) {
6122 			do {
6123 				randval = algo->range(status, 0, ht->nNumUsed - 1);
6124 				if (EG(exception)) {
6125 					return false;
6126 				}
6127 				zv = &ht->arPacked[randval];
6128 				if (!Z_ISUNDEF_P(zv)) {
6129 					ZVAL_LONG(retval, randval);
6130 					return true;
6131 				}
6132 			} while (true);
6133 		} else {
6134 			do {
6135 				randval = algo->range(status, 0, ht->nNumUsed - 1);
6136 				if (EG(exception)) {
6137 					return false;
6138 				}
6139 				b = &ht->arData[randval];
6140 				if (!Z_ISUNDEF(b->val)) {
6141 					if (b->key) {
6142 						ZVAL_STR_COPY(retval, b->key);
6143 					} else {
6144 						ZVAL_LONG(retval, b->h);
6145 					}
6146 					return true;
6147 				}
6148 			} while (true);
6149 		}
6150 	}
6151 
6152 	if (num_req <= 0 || num_req > num_avail) {
6153 		if (!silent) {
6154 			zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6155 		}
6156 		return false;
6157 	}
6158 
6159 	/* Make the return value an array only if we need to pass back more than one result. */
6160 	array_init_size(retval, (uint32_t) num_req);
6161 	if (num_req > (num_avail >> 1)) {
6162 		negative_bitset = 1;
6163 		num_req = num_avail - num_req;
6164 	}
6165 
6166 	bitset_len = zend_bitset_len(num_avail);
6167 	bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6168 	zend_bitset_clear(bitset, bitset_len);
6169 
6170 	i = num_req;
6171 	int failures = 0;
6172 	while (i) {
6173 		randval = algo->range(status, 0, num_avail - 1);
6174 		if (EG(exception)) {
6175 			goto fail;
6176 		}
6177 		if (zend_bitset_in(bitset, randval)) {
6178 			if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6179 				if (!silent) {
6180 					zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6181 				}
6182 
6183 				goto fail;
6184 			}
6185 		} else {
6186 			zend_bitset_incl(bitset, randval);
6187 			i--;
6188 			failures = 0;
6189 		}
6190 	}
6191 
6192 	zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6193 	ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6194 		/* We can't use zend_hash_index_find()
6195 		 * because the array may have string keys or gaps. */
6196 		ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6197 			if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6198 				if (string_key) {
6199 					ZEND_HASH_FILL_SET_STR_COPY(string_key);
6200 				} else {
6201 					ZEND_HASH_FILL_SET_LONG(num_key);
6202 				}
6203 				ZEND_HASH_FILL_NEXT();
6204 			}
6205 			i++;
6206 		} ZEND_HASH_FOREACH_END();
6207 	} ZEND_HASH_FILL_END();
6208 
6209 	free_alloca(bitset, use_heap);
6210 
6211 	return true;
6212 
6213  fail:
6214 	free_alloca(bitset, use_heap);
6215 
6216 	return false;
6217 }
6218 /* }}} */
6219 
6220 /* {{{ Return key/keys for random entry/entries in the array */
6221 PHP_FUNCTION(array_rand)
6222 {
6223 	zval *input;
6224 	zend_long num_req = 1;
6225 
6226 	ZEND_PARSE_PARAMETERS_START(1, 2)
6227 		Z_PARAM_ARRAY(input)
6228 		Z_PARAM_OPTIONAL
6229 		Z_PARAM_LONG(num_req)
6230 	ZEND_PARSE_PARAMETERS_END();
6231 
6232 	if (!php_array_pick_keys(
6233 			php_random_default_algo(),
6234 			php_random_default_status(),
6235 			input,
6236 			num_req,
6237 			return_value,
6238 			false)
6239 	) {
6240 		RETURN_THROWS();
6241 	}
6242 }
6243 /* }}} */
6244 
6245 /* Wrapper for array_sum and array_product */
6246 static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6247 {
6248 	HashTable *input;
6249 	zval *entry;
6250 
6251 	ZEND_PARSE_PARAMETERS_START(1, 1)
6252 		Z_PARAM_ARRAY_HT(input)
6253 	ZEND_PARSE_PARAMETERS_END();
6254 
6255 	if (zend_hash_num_elements(input) == 0) {
6256 		RETURN_LONG(initial);
6257 	}
6258 
6259 	ZVAL_LONG(return_value, initial);
6260 	ZEND_HASH_FOREACH_VAL(input, entry) {
6261 		/* For objects we try to cast them to a numeric type */
6262 		if (Z_TYPE_P(entry) == IS_OBJECT) {
6263 			zval dst;
6264 			zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6265 
6266 			/* Do not type error for BC */
6267 			if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6268 				php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6269 					op_name, zend_zval_type_name(entry));
6270 				continue;
6271 			}
6272 			op(return_value, return_value, &dst);
6273 			continue;
6274 		}
6275 
6276 		zend_result status = op(return_value, return_value, entry);
6277 		if (status == FAILURE) {
6278 			ZEND_ASSERT(EG(exception));
6279 			zend_clear_exception();
6280 			/* BC resources: previously resources were cast to int */
6281 			if (Z_TYPE_P(entry) == IS_RESOURCE) {
6282 				zval tmp;
6283 				ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6284 				op(return_value, return_value, &tmp);
6285 			}
6286 			/* BC non numeric strings: previously were cast to 0 */
6287 			else if (Z_TYPE_P(entry) == IS_STRING) {
6288 				zval tmp;
6289 				ZVAL_LONG(&tmp, 0);
6290 				op(return_value, return_value, &tmp);
6291 			}
6292 			php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6293 				op_name, zend_zval_type_name(entry));
6294 		}
6295 	} ZEND_HASH_FOREACH_END();
6296 }
6297 
6298 /* {{{ Returns the sum of the array entries */
6299 PHP_FUNCTION(array_sum)
6300 {
6301 	php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6302 }
6303 /* }}} */
6304 
6305 /* {{{ Returns the product of the array entries */
6306 PHP_FUNCTION(array_product)
6307 {
6308 	php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6309 }
6310 /* }}} */
6311 
6312 /* {{{ Iteratively reduce the array to a single value via the callback. */
6313 PHP_FUNCTION(array_reduce)
6314 {
6315 	zval *input;
6316 	zval args[2];
6317 	zval *operand;
6318 	zval retval;
6319 	zend_fcall_info fci;
6320 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6321 	zval *initial = NULL;
6322 	HashTable *htbl;
6323 
6324 	ZEND_PARSE_PARAMETERS_START(2, 3)
6325 		Z_PARAM_ARRAY(input)
6326 		Z_PARAM_FUNC(fci, fci_cache)
6327 		Z_PARAM_OPTIONAL
6328 		Z_PARAM_ZVAL(initial)
6329 	ZEND_PARSE_PARAMETERS_END();
6330 
6331 
6332 	if (ZEND_NUM_ARGS() > 2) {
6333 		ZVAL_COPY(return_value, initial);
6334 	} else {
6335 		ZVAL_NULL(return_value);
6336 	}
6337 
6338 	/* (zval **)input points to an element of argument stack
6339 	 * the base pointer of which is subject to change.
6340 	 * thus we need to keep the pointer to the hashtable for safety */
6341 	htbl = Z_ARRVAL_P(input);
6342 
6343 	if (zend_hash_num_elements(htbl) == 0) {
6344 		return;
6345 	}
6346 
6347 	fci.retval = &retval;
6348 	fci.param_count = 2;
6349 
6350 	ZEND_HASH_FOREACH_VAL(htbl, operand) {
6351 		ZVAL_COPY_VALUE(&args[0], return_value);
6352 		ZVAL_COPY(&args[1], operand);
6353 		fci.params = args;
6354 
6355 		if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6356 			zval_ptr_dtor(&args[1]);
6357 			zval_ptr_dtor(&args[0]);
6358 			ZVAL_COPY_VALUE(return_value, &retval);
6359 			if (UNEXPECTED(Z_ISREF_P(return_value))) {
6360 				zend_unwrap_reference(return_value);
6361 			}
6362 		} else {
6363 			zval_ptr_dtor(&args[1]);
6364 			zval_ptr_dtor(&args[0]);
6365 			RETURN_NULL();
6366 		}
6367 	} ZEND_HASH_FOREACH_END();
6368 }
6369 /* }}} */
6370 
6371 /* {{{ Filters elements from the array via the callback. */
6372 PHP_FUNCTION(array_filter)
6373 {
6374 	zval *array;
6375 	zval *operand;
6376 	zval *key;
6377 	zval args[2];
6378 	zval retval;
6379 	bool have_callback = 0;
6380 	zend_long use_type = 0;
6381 	zend_string *string_key;
6382 	zend_fcall_info fci = empty_fcall_info;
6383 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6384 	zend_ulong num_key;
6385 
6386 	ZEND_PARSE_PARAMETERS_START(1, 3)
6387 		Z_PARAM_ARRAY(array)
6388 		Z_PARAM_OPTIONAL
6389 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6390 		Z_PARAM_LONG(use_type)
6391 	ZEND_PARSE_PARAMETERS_END();
6392 
6393 	if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6394 		RETVAL_EMPTY_ARRAY();
6395 		return;
6396 	}
6397 	array_init(return_value);
6398 
6399 	if (ZEND_FCI_INITIALIZED(fci)) {
6400 		have_callback = 1;
6401 		fci.retval = &retval;
6402 		if (use_type == ARRAY_FILTER_USE_BOTH) {
6403 			fci.param_count = 2;
6404 			key = &args[1];
6405 		} else {
6406 			fci.param_count = 1;
6407 			key = &args[0];
6408 		}
6409 	}
6410 
6411 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6412 		if (have_callback) {
6413 			if (use_type) {
6414 				/* Set up the key */
6415 				if (!string_key) {
6416 					ZVAL_LONG(key, num_key);
6417 				} else {
6418 					ZVAL_STR_COPY(key, string_key);
6419 				}
6420 			}
6421 			if (use_type != ARRAY_FILTER_USE_KEY) {
6422 				ZVAL_COPY(&args[0], operand);
6423 			}
6424 			fci.params = args;
6425 
6426 			if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6427 				int retval_true;
6428 
6429 				zval_ptr_dtor(&args[0]);
6430 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6431 					zval_ptr_dtor(&args[1]);
6432 				}
6433 				retval_true = zend_is_true(&retval);
6434 				zval_ptr_dtor(&retval);
6435 				if (!retval_true) {
6436 					continue;
6437 				}
6438 			} else {
6439 				zval_ptr_dtor(&args[0]);
6440 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6441 					zval_ptr_dtor(&args[1]);
6442 				}
6443 				return;
6444 			}
6445 		} else if (!zend_is_true(operand)) {
6446 			continue;
6447 		}
6448 
6449 		if (string_key) {
6450 			operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6451 		} else {
6452 			operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6453 		}
6454 		zval_add_ref(operand);
6455 	} ZEND_HASH_FOREACH_END();
6456 }
6457 /* }}} */
6458 
6459 /* {{{ Applies the callback to the elements in given arrays. */
6460 PHP_FUNCTION(array_map)
6461 {
6462 	zval *arrays = NULL;
6463 	int n_arrays = 0;
6464 	zval result;
6465 	zend_fcall_info fci = empty_fcall_info;
6466 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6467 	int i;
6468 	uint32_t k, maxlen = 0;
6469 
6470 	ZEND_PARSE_PARAMETERS_START(2, -1)
6471 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6472 		Z_PARAM_VARIADIC('+', arrays, n_arrays)
6473 	ZEND_PARSE_PARAMETERS_END();
6474 
6475 	if (n_arrays == 1) {
6476 		zend_ulong num_key;
6477 		zend_string *str_key;
6478 		zval *zv, arg;
6479 		int ret;
6480 
6481 		if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6482 			zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6483 			RETURN_THROWS();
6484 		}
6485 		maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6486 
6487 		/* Short-circuit: if no callback and only one array, just return it. */
6488 		if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6489 			ZVAL_COPY(return_value, &arrays[0]);
6490 			return;
6491 		}
6492 
6493 		array_init_size(return_value, maxlen);
6494 		zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6495 
6496 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6497 			fci.retval = &result;
6498 			fci.param_count = 1;
6499 			fci.params = &arg;
6500 
6501 			ZVAL_COPY(&arg, zv);
6502 			ret = zend_call_function(&fci, &fci_cache);
6503 			i_zval_ptr_dtor(&arg);
6504 			if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6505 				zend_array_destroy(Z_ARR_P(return_value));
6506 				RETURN_NULL();
6507 			}
6508 			if (str_key) {
6509 				_zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6510 			} else {
6511 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6512 			}
6513 		} ZEND_HASH_FOREACH_END();
6514 	} else {
6515 		uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6516 
6517 		for (i = 0; i < n_arrays; i++) {
6518 			if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6519 				zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6520 				efree(array_pos);
6521 				RETURN_THROWS();
6522 			}
6523 			if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6524 				maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6525 			}
6526 		}
6527 
6528 		array_init_size(return_value, maxlen);
6529 
6530 		if (!ZEND_FCI_INITIALIZED(fci)) {
6531 			zval zv;
6532 
6533 			/* We iterate through all the arrays at once. */
6534 			for (k = 0; k < maxlen; k++) {
6535 
6536 				/* If no callback, the result will be an array, consisting of current
6537 				 * entries from all arrays. */
6538 				array_init_size(&result, n_arrays);
6539 
6540 				for (i = 0; i < n_arrays; i++) {
6541 					/* If this array still has elements, add the current one to the
6542 					 * parameter list, otherwise use null value. */
6543 					uint32_t pos = array_pos[i];
6544 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6545 						while (1) {
6546 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6547 								ZVAL_NULL(&zv);
6548 								break;
6549 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6550 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6551 								array_pos[i] = pos + 1;
6552 								break;
6553 							}
6554 							pos++;
6555 						}
6556 					} else {
6557 						while (1) {
6558 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6559 								ZVAL_NULL(&zv);
6560 								break;
6561 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6562 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6563 								array_pos[i] = pos + 1;
6564 								break;
6565 							}
6566 							pos++;
6567 						}
6568 					}
6569 					zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6570 				}
6571 
6572 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6573 			}
6574 		} else {
6575 			zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6576 
6577 			/* We iterate through all the arrays at once. */
6578 			for (k = 0; k < maxlen; k++) {
6579 				for (i = 0; i < n_arrays; i++) {
6580 					/* If this array still has elements, add the current one to the
6581 					 * parameter list, otherwise use null value. */
6582 					uint32_t pos = array_pos[i];
6583 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6584 						while (1) {
6585 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6586 								ZVAL_NULL(&params[i]);
6587 								break;
6588 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6589 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6590 								array_pos[i] = pos + 1;
6591 								break;
6592 							}
6593 							pos++;
6594 						}
6595 					} else {
6596 						while (1) {
6597 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6598 								ZVAL_NULL(&params[i]);
6599 								break;
6600 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6601 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6602 								array_pos[i] = pos + 1;
6603 								break;
6604 							}
6605 							pos++;
6606 						}
6607 					}
6608 				}
6609 
6610 				fci.retval = &result;
6611 				fci.param_count = n_arrays;
6612 				fci.params = params;
6613 
6614 				if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6615 					efree(array_pos);
6616 					zend_array_destroy(Z_ARR_P(return_value));
6617 					for (i = 0; i < n_arrays; i++) {
6618 						zval_ptr_dtor(&params[i]);
6619 					}
6620 					efree(params);
6621 					RETURN_NULL();
6622 				} else {
6623 					for (i = 0; i < n_arrays; i++) {
6624 						zval_ptr_dtor(&params[i]);
6625 					}
6626 				}
6627 
6628 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6629 			}
6630 
6631 			efree(params);
6632 		}
6633 		efree(array_pos);
6634 	}
6635 }
6636 /* }}} */
6637 
6638 /* {{{ Checks if the given key or index exists in the array */
6639 PHP_FUNCTION(array_key_exists)
6640 {
6641 	zval *key;
6642 	HashTable *ht;
6643 
6644 	ZEND_PARSE_PARAMETERS_START(2, 2)
6645 		Z_PARAM_ZVAL(key)
6646 		Z_PARAM_ARRAY_HT(ht)
6647 	ZEND_PARSE_PARAMETERS_END();
6648 
6649 	switch (Z_TYPE_P(key)) {
6650 		case IS_STRING:
6651 			RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6652 			break;
6653 		case IS_LONG:
6654 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6655 			break;
6656 		case IS_NULL:
6657 			RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6658 			break;
6659 		case IS_DOUBLE:
6660 			RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6661 			break;
6662 		case IS_FALSE:
6663 			RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6664 			break;
6665 		case IS_TRUE:
6666 			RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6667 			break;
6668 		case IS_RESOURCE:
6669 			zend_use_resource_as_offset(key);
6670 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6671 			break;
6672 		default:
6673 			zend_argument_type_error(1, "must be a valid array offset type");
6674 			break;
6675 	}
6676 }
6677 /* }}} */
6678 
6679 /* {{{ Split array into chunks */
6680 PHP_FUNCTION(array_chunk)
6681 {
6682 	int num_in;
6683 	zend_long size, current = 0;
6684 	zend_string *str_key;
6685 	zend_ulong num_key;
6686 	bool preserve_keys = 0;
6687 	zval *input = NULL;
6688 	zval chunk;
6689 	zval *entry;
6690 
6691 	ZEND_PARSE_PARAMETERS_START(2, 3)
6692 		Z_PARAM_ARRAY(input)
6693 		Z_PARAM_LONG(size)
6694 		Z_PARAM_OPTIONAL
6695 		Z_PARAM_BOOL(preserve_keys)
6696 	ZEND_PARSE_PARAMETERS_END();
6697 
6698 	/* Do bounds checking for size parameter. */
6699 	if (size < 1) {
6700 		zend_argument_value_error(2, "must be greater than 0");
6701 		RETURN_THROWS();
6702 	}
6703 
6704 	num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6705 
6706 	if (size > num_in) {
6707 		if (num_in == 0) {
6708 			RETVAL_EMPTY_ARRAY();
6709 			return;
6710 		}
6711 		size = num_in;
6712 	}
6713 
6714 	array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6715 
6716 	ZVAL_UNDEF(&chunk);
6717 
6718 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6719 		/* If new chunk, create and initialize it. */
6720 		if (Z_TYPE(chunk) == IS_UNDEF) {
6721 			array_init_size(&chunk, (uint32_t)size);
6722 		}
6723 
6724 		/* Add entry to the chunk, preserving keys if necessary. */
6725 		if (preserve_keys) {
6726 			if (str_key) {
6727 				entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6728 			} else {
6729 				entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6730 			}
6731 		} else {
6732 			entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6733 		}
6734 		zval_add_ref(entry);
6735 
6736 		/* If reached the chunk size, add it to the result array, and reset the
6737 		 * pointer. */
6738 		if (!(++current % size)) {
6739 			add_next_index_zval(return_value, &chunk);
6740 			ZVAL_UNDEF(&chunk);
6741 		}
6742 	} ZEND_HASH_FOREACH_END();
6743 
6744 	/* Add the final chunk if there is one. */
6745 	if (Z_TYPE(chunk) != IS_UNDEF) {
6746 		add_next_index_zval(return_value, &chunk);
6747 	}
6748 }
6749 /* }}} */
6750 
6751 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6752 PHP_FUNCTION(array_combine)
6753 {
6754 	HashTable *values, *keys;
6755 	uint32_t pos_values = 0;
6756 	zval *entry_keys, *entry_values;
6757 	int num_keys, num_values;
6758 
6759 	ZEND_PARSE_PARAMETERS_START(2, 2)
6760 		Z_PARAM_ARRAY_HT(keys)
6761 		Z_PARAM_ARRAY_HT(values)
6762 	ZEND_PARSE_PARAMETERS_END();
6763 
6764 	num_keys = zend_hash_num_elements(keys);
6765 	num_values = zend_hash_num_elements(values);
6766 
6767 	if (num_keys != num_values) {
6768 		zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6769 		RETURN_THROWS();
6770 	}
6771 
6772 	if (!num_keys) {
6773 		RETURN_EMPTY_ARRAY();
6774 	}
6775 
6776 	array_init_size(return_value, num_keys);
6777 	ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6778 		while (1) {
6779 			if (pos_values >= values->nNumUsed) {
6780 				break;
6781 			}
6782 			entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6783 			if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6784 				if (Z_TYPE_P(entry_keys) == IS_LONG) {
6785 					entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6786 						Z_LVAL_P(entry_keys), entry_values);
6787 				} else {
6788 					zend_string *tmp_key;
6789 					zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6790 					entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6791 						key, entry_values);
6792 					zend_tmp_string_release(tmp_key);
6793 				}
6794 				zval_add_ref(entry_values);
6795 				pos_values++;
6796 				break;
6797 			}
6798 			pos_values++;
6799 		}
6800 	} ZEND_HASH_FOREACH_END();
6801 }
6802 /* }}} */
6803