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