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