xref: /PHP-8.4/ext/standard/array.c (revision 7bbf2eae)
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_array_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
704 	} else {
705 		zend_array_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_array_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_array_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_array_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_array_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_array_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 				dest_zval = dest_entry;
4041 
4042 				if (Z_TYPE_P(dest_zval) == IS_NULL) {
4043 					convert_to_array(dest_zval);
4044 					add_next_index_null(dest_zval);
4045 				} else {
4046 					convert_to_array(dest_zval);
4047 				}
4048 				SEPARATE_ZVAL(dest_zval);
4049 
4050 				ZVAL_UNDEF(&tmp);
4051 				if (Z_TYPE_P(src_zval) == IS_OBJECT) {
4052 					ZVAL_COPY(&tmp, src_zval);
4053 					convert_to_array(&tmp);
4054 					src_zval = &tmp;
4055 				}
4056 				if (Z_TYPE_P(src_zval) == IS_ARRAY) {
4057 					if (thash) {
4058 						GC_TRY_PROTECT_RECURSION(thash);
4059 					}
4060 					ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4061 					if (thash) {
4062 						GC_TRY_UNPROTECT_RECURSION(thash);
4063 					}
4064 					if (!ret) {
4065 						return 0;
4066 					}
4067 				} else {
4068 					Z_TRY_ADDREF_P(src_zval);
4069 					zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
4070 					if (EXPECTED(!zv)) {
4071 						Z_TRY_DELREF_P(src_zval);
4072 						zend_cannot_add_element();
4073 						return 0;
4074 					}
4075 				}
4076 				zval_ptr_dtor(&tmp);
4077 			} else {
4078 				zval *zv = zend_hash_add_new(dest, string_key, src_entry);
4079 				zval_add_ref(zv);
4080 			}
4081 		} else {
4082 			zval *zv = zend_hash_next_index_insert(dest, src_entry);
4083 			if (UNEXPECTED(!zv)) {
4084 				zend_cannot_add_element();
4085 				return 0;
4086 			}
4087 			zval_add_ref(zv);
4088 		}
4089 	} ZEND_HASH_FOREACH_END();
4090 	return 1;
4091 }
4092 /* }}} */
4093 
4094 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
4095 {
4096 	zval *src_entry;
4097 	zend_string *string_key;
4098 
4099 	if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
4100 		zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
4101 		ZEND_HASH_FILL_PACKED(dest) {
4102 			ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4103 				if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
4104 					UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
4105 					src_entry = Z_REFVAL_P(src_entry);
4106 				}
4107 				Z_TRY_ADDREF_P(src_entry);
4108 				ZEND_HASH_FILL_ADD(src_entry);
4109 			} ZEND_HASH_FOREACH_END();
4110 		} ZEND_HASH_FILL_END();
4111 	} else {
4112 		ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4113 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4114 				Z_REFCOUNT_P(src_entry) == 1)) {
4115 				src_entry = Z_REFVAL_P(src_entry);
4116 			}
4117 			Z_TRY_ADDREF_P(src_entry);
4118 			if (string_key) {
4119 				zend_hash_update(dest, string_key, src_entry);
4120 			} else {
4121 				zend_hash_next_index_insert_new(dest, src_entry);
4122 			}
4123 		} ZEND_HASH_FOREACH_END();
4124 	}
4125 	return 1;
4126 }
4127 /* }}} */
4128 
4129 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4130 {
4131 	zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4132 	zend_string *string_key;
4133 	zend_ulong num_key;
4134 	int ret;
4135 
4136 	ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4137 		src_zval = src_entry;
4138 		ZVAL_DEREF(src_zval);
4139 		if (string_key) {
4140 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4141 				(dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4142 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
4143 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4144 
4145 				zval *zv = zend_hash_update(dest, string_key, src_entry);
4146 				zval_add_ref(zv);
4147 				continue;
4148 			}
4149 		} else {
4150 			if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4151 				(dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4152 				(Z_TYPE_P(dest_entry) != IS_ARRAY &&
4153 				 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4154 
4155 				zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4156 				zval_add_ref(zv);
4157 				continue;
4158 			}
4159 		}
4160 
4161 		dest_zval = dest_entry;
4162 		ZVAL_DEREF(dest_zval);
4163 		if (Z_IS_RECURSIVE_P(dest_zval) ||
4164 			Z_IS_RECURSIVE_P(src_zval) ||
4165 			(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))) {
4166 			zend_throw_error(NULL, "Recursion detected");
4167 			return 0;
4168 		}
4169 
4170 		ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4171 		SEPARATE_ZVAL(dest_entry);
4172 		dest_zval = dest_entry;
4173 
4174 		if (Z_REFCOUNTED_P(dest_zval)) {
4175 			Z_PROTECT_RECURSION_P(dest_zval);
4176 		}
4177 		if (Z_REFCOUNTED_P(src_zval)) {
4178 			Z_PROTECT_RECURSION_P(src_zval);
4179 		}
4180 
4181 		ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4182 
4183 		if (Z_REFCOUNTED_P(dest_zval)) {
4184 			Z_UNPROTECT_RECURSION_P(dest_zval);
4185 		}
4186 		if (Z_REFCOUNTED_P(src_zval)) {
4187 			Z_UNPROTECT_RECURSION_P(src_zval);
4188 		}
4189 
4190 		if (!ret) {
4191 			return 0;
4192 		}
4193 	} ZEND_HASH_FOREACH_END();
4194 
4195 	return 1;
4196 }
4197 /* }}} */
4198 
4199 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4200 {
4201 	zval *args = NULL;
4202 	zval *arg;
4203 	uint32_t argc, i;
4204 	HashTable *dest;
4205 
4206 	ZEND_PARSE_PARAMETERS_START(1, -1)
4207 		Z_PARAM_VARIADIC('+', args, argc)
4208 	ZEND_PARSE_PARAMETERS_END();
4209 
4210 
4211 	for (i = 0; i < argc; i++) {
4212 		zval *arg = args + i;
4213 
4214 		if (Z_TYPE_P(arg) != IS_ARRAY) {
4215 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4216 			RETURN_THROWS();
4217 		}
4218 	}
4219 
4220 	/* copy first array if necessary */
4221 	arg = args;
4222 	bool in_place = zend_may_modify_arg_in_place(arg);
4223 	if (in_place) {
4224 		dest = Z_ARRVAL_P(arg);
4225 	} else {
4226 		dest = zend_array_dup(Z_ARRVAL_P(arg));
4227 	}
4228 
4229 	ZVAL_ARR(return_value, dest);
4230 
4231 	if (recursive) {
4232 		for (i = 1; i < argc; i++) {
4233 			arg = args + i;
4234 			php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4235 		}
4236 	} else {
4237 		for (i = 1; i < argc; i++) {
4238 			arg = args + i;
4239 			zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4240 		}
4241 	}
4242 
4243 	if (in_place) {
4244 		GC_ADDREF(dest);
4245 	}
4246 }
4247 /* }}} */
4248 
4249 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4250 {
4251 	zval *args = NULL;
4252 	zval *arg;
4253 	uint32_t argc, i;
4254 	zval *src_entry;
4255 	HashTable *src, *dest;
4256 	uint32_t count = 0;
4257 
4258 	ZEND_PARSE_PARAMETERS_START(0, -1)
4259 		Z_PARAM_VARIADIC('+', args, argc)
4260 	ZEND_PARSE_PARAMETERS_END();
4261 
4262 	if (argc == 0) {
4263 		RETURN_EMPTY_ARRAY();
4264 	}
4265 
4266 	for (i = 0; i < argc; i++) {
4267 		zval *arg = args + i;
4268 
4269 		if (Z_TYPE_P(arg) != IS_ARRAY) {
4270 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4271 			RETURN_THROWS();
4272 		}
4273 		count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4274 	}
4275 
4276 	if (argc == 2) {
4277 		zval *ret = NULL;
4278 
4279 		if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4280 			ret = &args[1];
4281 		} else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4282 			ret = &args[0];
4283 		}
4284 		if (ret) {
4285 			if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4286 				if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4287 					ZVAL_COPY(return_value, ret);
4288 					return;
4289 				}
4290 			} else {
4291 				bool copy = 1;
4292 				zend_string *string_key;
4293 
4294 				ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4295 					if (!string_key) {
4296 						copy = 0;
4297 						break;
4298 					}
4299 				} ZEND_HASH_FOREACH_END();
4300 				if (copy) {
4301 					ZVAL_COPY(return_value, ret);
4302 					return;
4303 				}
4304 			}
4305 		}
4306 	}
4307 
4308 	arg = args;
4309 	src  = Z_ARRVAL_P(arg);
4310 	/* copy first array if necessary */
4311 	bool in_place = false;
4312 	if (HT_IS_PACKED(src)) {
4313 		/* Note: If it has holes, it might get sequentialized */
4314 		if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4315 			dest = src;
4316 			in_place = true;
4317 			ZVAL_ARR(return_value, dest);
4318 		} else {
4319 			array_init_size(return_value, count);
4320 			dest = Z_ARRVAL_P(return_value);
4321 
4322 			zend_hash_real_init_packed(dest);
4323 			ZEND_HASH_FILL_PACKED(dest) {
4324 				ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4325 					if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4326 						Z_REFCOUNT_P(src_entry) == 1)) {
4327 						src_entry = Z_REFVAL_P(src_entry);
4328 					}
4329 					Z_TRY_ADDREF_P(src_entry);
4330 					ZEND_HASH_FILL_ADD(src_entry);
4331 				} ZEND_HASH_FOREACH_END();
4332 			} ZEND_HASH_FILL_END();
4333 		}
4334 	} else {
4335 		array_init_size(return_value, count);
4336 		dest = Z_ARRVAL_P(return_value);
4337 
4338 		zend_string *string_key;
4339 		zend_hash_real_init_mixed(dest);
4340 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4341 			if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4342 				Z_REFCOUNT_P(src_entry) == 1)) {
4343 				src_entry = Z_REFVAL_P(src_entry);
4344 			}
4345 			Z_TRY_ADDREF_P(src_entry);
4346 			if (EXPECTED(string_key)) {
4347 				_zend_hash_append(dest, string_key, src_entry);
4348 			} else {
4349 				zend_hash_next_index_insert_new(dest, src_entry);
4350 			}
4351 		} ZEND_HASH_FOREACH_END();
4352 	}
4353 	if (recursive) {
4354 		for (i = 1; i < argc; i++) {
4355 			arg = args + i;
4356 			php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4357 		}
4358 	} else {
4359 		for (i = 1; i < argc; i++) {
4360 			arg = args + i;
4361 			php_array_merge(dest, Z_ARRVAL_P(arg));
4362 		}
4363 	}
4364 
4365 	if (in_place) {
4366 		GC_ADDREF(dest);
4367 	}
4368 }
4369 /* }}} */
4370 
4371 /* {{{ Merges elements from passed arrays into one array */
4372 PHP_FUNCTION(array_merge)
4373 {
4374 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4375 }
4376 /* }}} */
4377 
4378 /* {{{ Recursively merges elements from passed arrays into one array */
4379 PHP_FUNCTION(array_merge_recursive)
4380 {
4381 	php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4382 }
4383 /* }}} */
4384 
4385 /* {{{ Replaces elements from passed arrays into one array */
4386 PHP_FUNCTION(array_replace)
4387 {
4388 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4389 }
4390 /* }}} */
4391 
4392 /* {{{ Recursively replaces elements from passed arrays into one array */
4393 PHP_FUNCTION(array_replace_recursive)
4394 {
4395 	php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4396 }
4397 /* }}} */
4398 
4399 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4400 PHP_FUNCTION(array_keys)
4401 {
4402 	zval *input,				/* Input array */
4403 	     *search_value = NULL,	/* Value to search for */
4404 	     *entry,				/* An entry in the input array */
4405 	       new_val;				/* New value */
4406 	bool strict = 0;		/* do strict comparison */
4407 	zend_ulong num_idx;
4408 	zend_string *str_idx;
4409 	zend_array *arrval;
4410 	zend_ulong elem_count;
4411 
4412 	ZEND_PARSE_PARAMETERS_START(1, 3)
4413 		Z_PARAM_ARRAY(input)
4414 		Z_PARAM_OPTIONAL
4415 		Z_PARAM_ZVAL(search_value)
4416 		Z_PARAM_BOOL(strict)
4417 	ZEND_PARSE_PARAMETERS_END();
4418 	arrval = Z_ARRVAL_P(input);
4419 	elem_count = zend_hash_num_elements(arrval);
4420 
4421 	/* Base case: empty input */
4422 	if (!elem_count) {
4423 		RETURN_COPY(input);
4424 	}
4425 
4426 	/* Initialize return array */
4427 	if (search_value != NULL) {
4428 		array_init(return_value);
4429 
4430 		if (strict) {
4431 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4432 				ZVAL_DEREF(entry);
4433 				if (fast_is_identical_function(search_value, entry)) {
4434 					if (str_idx) {
4435 						ZVAL_STR_COPY(&new_val, str_idx);
4436 					} else {
4437 						ZVAL_LONG(&new_val, num_idx);
4438 					}
4439 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4440 				}
4441 			} ZEND_HASH_FOREACH_END();
4442 		} else {
4443 			ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4444 				if (fast_equal_check_function(search_value, entry)) {
4445 					if (str_idx) {
4446 						ZVAL_STR_COPY(&new_val, str_idx);
4447 					} else {
4448 						ZVAL_LONG(&new_val, num_idx);
4449 					}
4450 					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4451 				}
4452 			} ZEND_HASH_FOREACH_END();
4453 		}
4454 	} else {
4455 		array_init_size(return_value, elem_count);
4456 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4457 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4458 			if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4459 				/* Optimistic case: range(0..n-1) for vector-like packed array */
4460 				zend_ulong lval = 0;
4461 
4462 				for (; lval < elem_count; ++lval) {
4463 					ZEND_HASH_FILL_SET_LONG(lval);
4464 					ZEND_HASH_FILL_NEXT();
4465 				}
4466 			} else {
4467 				/* Go through input array and add keys to the return array */
4468 				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4469 					if (str_idx) {
4470 						ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4471 					} else {
4472 						ZEND_HASH_FILL_SET_LONG(num_idx);
4473 					}
4474 					ZEND_HASH_FILL_NEXT();
4475 				} ZEND_HASH_FOREACH_END();
4476 			}
4477 		} ZEND_HASH_FILL_END();
4478 	}
4479 }
4480 /* }}} */
4481 
4482 /* {{{ Get the key of the first element of the array */
4483 PHP_FUNCTION(array_key_first)
4484 {
4485 	zval *stack;    /* Input stack */
4486 
4487 	ZEND_PARSE_PARAMETERS_START(1, 1)
4488 		Z_PARAM_ARRAY(stack)
4489 	ZEND_PARSE_PARAMETERS_END();
4490 
4491 	HashTable *target_hash = Z_ARRVAL_P (stack);
4492 	HashPosition pos = 0;
4493 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4494 }
4495 /* }}} */
4496 
4497 /* {{{ Get the key of the last element of the array */
4498 PHP_FUNCTION(array_key_last)
4499 {
4500 	zval *stack;    /* Input stack */
4501 	HashPosition pos;
4502 
4503 	ZEND_PARSE_PARAMETERS_START(1, 1)
4504 		Z_PARAM_ARRAY(stack)
4505 	ZEND_PARSE_PARAMETERS_END();
4506 
4507 	HashTable *target_hash = Z_ARRVAL_P (stack);
4508 	zend_hash_internal_pointer_end_ex(target_hash, &pos);
4509 	zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4510 }
4511 /* }}} */
4512 
4513 /* {{{ Return just the values from the input array */
4514 PHP_FUNCTION(array_values)
4515 {
4516 	zval	 *input;		/* Input array */
4517 	zend_array *arrval;
4518 	zend_long arrlen;
4519 
4520 	ZEND_PARSE_PARAMETERS_START(1, 1)
4521 		Z_PARAM_ARRAY(input)
4522 	ZEND_PARSE_PARAMETERS_END();
4523 
4524 	arrval = Z_ARRVAL_P(input);
4525 
4526 	/* Return empty input as is */
4527 	arrlen = zend_hash_num_elements(arrval);
4528 	if (!arrlen) {
4529 		RETURN_EMPTY_ARRAY();
4530 	}
4531 
4532 	/* Return vector-like packed arrays as-is */
4533 	if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4534 		arrval->nNextFreeElement == arrlen) {
4535 		RETURN_COPY(input);
4536 	}
4537 
4538 	RETURN_ARR(zend_array_to_list(arrval));
4539 }
4540 /* }}} */
4541 
4542 /* {{{ Return the value as key and the frequency of that value in input as value */
4543 PHP_FUNCTION(array_count_values)
4544 {
4545 	zval	*input,		/* Input array */
4546 			*entry,		/* An entry in the input array */
4547 			*tmp;
4548 	HashTable *myht;
4549 
4550 	ZEND_PARSE_PARAMETERS_START(1, 1)
4551 		Z_PARAM_ARRAY(input)
4552 	ZEND_PARSE_PARAMETERS_END();
4553 
4554 	/* Initialize return array */
4555 	array_init(return_value);
4556 
4557 	/* Go through input array and add values to the return array */
4558 	myht = Z_ARRVAL_P(input);
4559 	ZEND_HASH_FOREACH_VAL(myht, entry) {
4560 		ZVAL_DEREF(entry);
4561 		if (Z_TYPE_P(entry) == IS_LONG) {
4562 			if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4563 				zval data;
4564 				ZVAL_LONG(&data, 1);
4565 				zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4566 			} else {
4567 				Z_LVAL_P(tmp)++;
4568 			}
4569 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4570 			if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4571 				zval data;
4572 				ZVAL_LONG(&data, 1);
4573 				zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4574 			} else {
4575 				Z_LVAL_P(tmp)++;
4576 			}
4577 		} else {
4578 			php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4579 		}
4580 	} ZEND_HASH_FOREACH_END();
4581 }
4582 /* }}} */
4583 
4584 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4585 {
4586 	zval *prop = NULL;
4587 
4588 	if (Z_TYPE_P(data) == IS_OBJECT) {
4589 		zend_string *tmp_str;
4590 		/* If name is an integer convert integer to string */
4591 		if (name_str == NULL) {
4592 			tmp_str = zend_long_to_str(name_long);
4593 		} else {
4594 			tmp_str = zend_string_copy(name_str);
4595 		}
4596 		/* The has_property check is first performed in "exists" mode (which returns true for
4597 		 * properties that are null but exist) and then in "has" mode to handle objects that
4598 		 * implement __isset (which is not called in "exists" mode). */
4599 		if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4600 				|| Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4601 			prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4602 			if (prop) {
4603 				ZVAL_DEREF(prop);
4604 				if (prop != rv) {
4605 					Z_TRY_ADDREF_P(prop);
4606 				}
4607 			}
4608 		}
4609 		zend_string_release(tmp_str);
4610 	} else if (Z_TYPE_P(data) == IS_ARRAY) {
4611 		/* Name is a string */
4612 		if (name_str != NULL) {
4613 			prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4614 		} else {
4615 			prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4616 		}
4617 		if (prop) {
4618 			ZVAL_DEREF(prop);
4619 			Z_TRY_ADDREF_P(prop);
4620 		}
4621 	}
4622 
4623 	return prop;
4624 }
4625 /* }}} */
4626 
4627 /* {{{ Return the values from a single column in the input array, identified by the
4628    value_key and optionally indexed by the index_key */
4629 PHP_FUNCTION(array_column)
4630 {
4631 	HashTable *input;
4632 	zval *colval, *data, rv;
4633 	zend_string *column_str = NULL;
4634 	zend_long column_long = 0;
4635 	bool column_is_null = 0;
4636 	zend_string *index_str = NULL;
4637 	zend_long index_long = 0;
4638 	bool index_is_null = 1;
4639 
4640 	ZEND_PARSE_PARAMETERS_START(2, 3)
4641 		Z_PARAM_ARRAY_HT(input)
4642 		Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4643 		Z_PARAM_OPTIONAL
4644 		Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4645 	ZEND_PARSE_PARAMETERS_END();
4646 
4647 	void* cache_slot_column[3] = { NULL, NULL, NULL };
4648 	void* cache_slot_index[3] = { NULL, NULL, NULL };
4649 
4650 	array_init_size(return_value, zend_hash_num_elements(input));
4651 	/* Index param is not passed */
4652 	if (index_is_null) {
4653 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4654 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4655 			ZEND_HASH_FOREACH_VAL(input, data) {
4656 				ZVAL_DEREF(data);
4657 				if (column_is_null) {
4658 					Z_TRY_ADDREF_P(data);
4659 					colval = data;
4660 				} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4661 					continue;
4662 				}
4663 				ZEND_HASH_FILL_ADD(colval);
4664 			} ZEND_HASH_FOREACH_END();
4665 		} ZEND_HASH_FILL_END();
4666 	} else {
4667 		ZEND_HASH_FOREACH_VAL(input, data) {
4668 			ZVAL_DEREF(data);
4669 
4670 			if (column_is_null) {
4671 				Z_TRY_ADDREF_P(data);
4672 				colval = data;
4673 			} else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4674 				continue;
4675 			}
4676 
4677 			zval rv;
4678 			zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4679 			if (keyval) {
4680 				array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4681 				zval_ptr_dtor(colval);
4682 				zval_ptr_dtor(keyval);
4683 			} else {
4684 				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4685 			}
4686 		} ZEND_HASH_FOREACH_END();
4687 	}
4688 }
4689 /* }}} */
4690 
4691 /* {{{ Return input as a new array with the order of the entries reversed */
4692 PHP_FUNCTION(array_reverse)
4693 {
4694 	zval	 *input,				/* Input array */
4695 			 *entry;				/* An entry in the input array */
4696 	zend_string *string_key;
4697 	zend_ulong	  num_key;
4698 	bool preserve_keys = 0;	/* whether to preserve keys */
4699 
4700 	ZEND_PARSE_PARAMETERS_START(1, 2)
4701 		Z_PARAM_ARRAY(input)
4702 		Z_PARAM_OPTIONAL
4703 		Z_PARAM_BOOL(preserve_keys)
4704 	ZEND_PARSE_PARAMETERS_END();
4705 
4706 	/* Initialize return array */
4707 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4708 	if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4709 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4710 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4711 			ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4712 				if (UNEXPECTED(Z_ISREF_P(entry) &&
4713 					Z_REFCOUNT_P(entry) == 1)) {
4714 					entry = Z_REFVAL_P(entry);
4715 				}
4716 				Z_TRY_ADDREF_P(entry);
4717 				ZEND_HASH_FILL_ADD(entry);
4718 			} ZEND_HASH_FOREACH_END();
4719 		} ZEND_HASH_FILL_END();
4720 	} else {
4721 		ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4722 			if (string_key) {
4723 				entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4724 			} else {
4725 				if (preserve_keys) {
4726 					entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4727 				} else {
4728 					entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4729 				}
4730 			}
4731 			zval_add_ref(entry);
4732 		} ZEND_HASH_FOREACH_END();
4733 	}
4734 }
4735 /* }}} */
4736 
4737 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4738 PHP_FUNCTION(array_pad)
4739 {
4740 	zval  *input;		/* Input array */
4741 	zval  *pad_value;	/* Padding value obviously */
4742 	zend_long pad_size;		/* Size to pad to */
4743 	zend_long pad_size_abs;	/* Absolute value of pad_size */
4744 	zend_long input_size;		/* Size of the input array */
4745 	zend_long num_pads;		/* How many pads do we need */
4746 	zend_long i;
4747 	zend_string *key;
4748 	zval *value;
4749 
4750 	ZEND_PARSE_PARAMETERS_START(3, 3)
4751 		Z_PARAM_ARRAY(input)
4752 		Z_PARAM_LONG(pad_size)
4753 		Z_PARAM_ZVAL(pad_value)
4754 	ZEND_PARSE_PARAMETERS_END();
4755 
4756 	if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4757 		zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4758 		RETURN_THROWS();
4759 	}
4760 
4761 	/* Do some initial calculations */
4762 	input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4763 	pad_size_abs = ZEND_ABS(pad_size);
4764 
4765 	if (input_size >= pad_size_abs) {
4766 		/* Copy the original array */
4767 		ZVAL_COPY(return_value, input);
4768 		return;
4769 	}
4770 
4771 	num_pads = pad_size_abs - input_size;
4772 	if (Z_REFCOUNTED_P(pad_value)) {
4773 		GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4774 	}
4775 
4776 	array_init_size(return_value, pad_size_abs);
4777 	if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4778 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4779 
4780 		if (pad_size < 0) {
4781 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4782 				for (i = 0; i < num_pads; i++) {
4783 					ZEND_HASH_FILL_ADD(pad_value);
4784 				}
4785 			} ZEND_HASH_FILL_END();
4786 		}
4787 
4788 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4789 			ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4790 				Z_TRY_ADDREF_P(value);
4791 				ZEND_HASH_FILL_ADD(value);
4792 			} ZEND_HASH_FOREACH_END();
4793 		} ZEND_HASH_FILL_END();
4794 
4795 		if (pad_size > 0) {
4796 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4797 				for (i = 0; i < num_pads; i++) {
4798 					ZEND_HASH_FILL_ADD(pad_value);
4799 				}
4800 			} ZEND_HASH_FILL_END();
4801 		}
4802 	} else {
4803 		if (pad_size < 0) {
4804 			for (i = 0; i < num_pads; i++) {
4805 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4806 			}
4807 		}
4808 
4809 		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4810 			Z_TRY_ADDREF_P(value);
4811 			if (key) {
4812 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4813 			} else {
4814 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4815 			}
4816 		} ZEND_HASH_FOREACH_END();
4817 
4818 		if (pad_size > 0) {
4819 			for (i = 0; i < num_pads; i++) {
4820 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4821 			}
4822 		}
4823 	}
4824 }
4825 /* }}} */
4826 
4827 /* {{{ Return array with key <-> value flipped */
4828 PHP_FUNCTION(array_flip)
4829 {
4830 	zval *array, *entry, data;
4831 	zend_ulong num_idx;
4832 	zend_string *str_idx;
4833 
4834 	ZEND_PARSE_PARAMETERS_START(1, 1)
4835 		Z_PARAM_ARRAY(array)
4836 	ZEND_PARSE_PARAMETERS_END();
4837 
4838 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4839 
4840 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4841 		ZVAL_DEREF(entry);
4842 		if (Z_TYPE_P(entry) == IS_LONG) {
4843 			if (str_idx) {
4844 				ZVAL_STR_COPY(&data, str_idx);
4845 			} else {
4846 				ZVAL_LONG(&data, num_idx);
4847 			}
4848 			zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4849 		} else if (Z_TYPE_P(entry) == IS_STRING) {
4850 			if (str_idx) {
4851 				ZVAL_STR_COPY(&data, str_idx);
4852 			} else {
4853 				ZVAL_LONG(&data, num_idx);
4854 			}
4855 			zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4856 		} else {
4857 			php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4858 		}
4859 	} ZEND_HASH_FOREACH_END();
4860 }
4861 /* }}} */
4862 
4863 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4864 PHP_FUNCTION(array_change_key_case)
4865 {
4866 	zval *array, *entry;
4867 	zend_string *string_key;
4868 	zend_string *new_key;
4869 	zend_ulong num_key;
4870 	zend_long change_to_upper=0;
4871 
4872 	ZEND_PARSE_PARAMETERS_START(1, 2)
4873 		Z_PARAM_ARRAY(array)
4874 		Z_PARAM_OPTIONAL
4875 		Z_PARAM_LONG(change_to_upper)
4876 	ZEND_PARSE_PARAMETERS_END();
4877 
4878 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4879 
4880 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4881 		if (!string_key) {
4882 			entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4883 		} else {
4884 			if (change_to_upper) {
4885 				new_key = zend_string_toupper(string_key);
4886 			} else {
4887 				new_key = zend_string_tolower(string_key);
4888 			}
4889 			entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4890 			zend_string_release_ex(new_key, 0);
4891 		}
4892 
4893 		zval_add_ref(entry);
4894 	} ZEND_HASH_FOREACH_END();
4895 }
4896 /* }}} */
4897 
4898 struct bucketindex {
4899 	Bucket b;
4900 	unsigned int i;
4901 };
4902 
4903 static void array_bucketindex_swap(void *p, void *q)
4904 {
4905 	struct bucketindex *f = (struct bucketindex *)p;
4906 	struct bucketindex *g = (struct bucketindex *)q;
4907 	struct bucketindex t;
4908 	t = *f;
4909 	*f = *g;
4910 	*g = t;
4911 }
4912 
4913 /* {{{ Removes duplicate values from array */
4914 PHP_FUNCTION(array_unique)
4915 {
4916 	zval *array;
4917 	Bucket *p;
4918 	zend_long sort_type = PHP_SORT_STRING;
4919 	bucket_compare_func_t cmp;
4920 	struct bucketindex *arTmp, *cmpdata, *lastkept;
4921 	uint32_t i, idx;
4922 
4923 	ZEND_PARSE_PARAMETERS_START(1, 2)
4924 		Z_PARAM_ARRAY(array)
4925 		Z_PARAM_OPTIONAL
4926 		Z_PARAM_LONG(sort_type)
4927 	ZEND_PARSE_PARAMETERS_END();
4928 
4929 	if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {	/* nothing to do */
4930 		ZVAL_COPY(return_value, array);
4931 		return;
4932 	}
4933 
4934 	if (sort_type == PHP_SORT_STRING) {
4935 		HashTable seen;
4936 		zend_long num_key;
4937 		zend_string *str_key;
4938 		zval *val;
4939 
4940 		zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4941 		array_init(return_value);
4942 
4943 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4944 			zval *retval;
4945 			if (Z_TYPE_P(val) == IS_STRING) {
4946 				retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4947 			} else {
4948 				zend_string *tmp_str_val;
4949 				zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4950 				retval = zend_hash_add_empty_element(&seen, str_val);
4951 				zend_tmp_string_release(tmp_str_val);
4952 			}
4953 
4954 			if (retval) {
4955 				/* First occurrence of the value */
4956 				if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4957 					ZVAL_DEREF(val);
4958 				}
4959 				Z_TRY_ADDREF_P(val);
4960 
4961 				if (str_key) {
4962 					zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4963 				} else {
4964 					zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4965 				}
4966 			}
4967 		} ZEND_HASH_FOREACH_END();
4968 
4969 		zend_hash_destroy(&seen);
4970 		return;
4971 	}
4972 
4973 	cmp = php_get_data_compare_func_unstable(sort_type, 0);
4974 
4975 	bool in_place = zend_may_modify_arg_in_place(array);
4976 	if (in_place) {
4977 		RETVAL_ARR(Z_ARRVAL_P(array));
4978 	} else {
4979 		RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4980 	}
4981 
4982 	/* create and sort array with pointers to the target_hash buckets */
4983 	arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4984 	if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4985 		zval *zv = Z_ARRVAL_P(array)->arPacked;
4986 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4987 			if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4988 			ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4989 			arTmp[i].b.h = idx;
4990 			arTmp[i].b.key = NULL;
4991 			arTmp[i].i = i;
4992 			i++;
4993 		}
4994 	} else {
4995 		p = Z_ARRVAL_P(array)->arData;
4996 		for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4997 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
4998 			arTmp[i].b = *p;
4999 			arTmp[i].i = i;
5000 			i++;
5001 		}
5002 	}
5003 	ZVAL_UNDEF(&arTmp[i].b.val);
5004 	zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
5005 			(compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
5006 	/* go through the sorted array and delete duplicates from the copy */
5007 	lastkept = arTmp;
5008 	for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
5009 		if (cmp(&lastkept->b, &cmpdata->b)) {
5010 			lastkept = cmpdata;
5011 		} else {
5012 			if (lastkept->i > cmpdata->i) {
5013 				p = &lastkept->b;
5014 				lastkept = cmpdata;
5015 			} else {
5016 				p = &cmpdata->b;
5017 			}
5018 			if (p->key == NULL) {
5019 				zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5020 			} else {
5021 				zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5022 			}
5023 		}
5024 	}
5025 	pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5026 
5027 	if (in_place) {
5028 		Z_ADDREF_P(return_value);
5029 	}
5030 }
5031 /* }}} */
5032 
5033 static int zval_compare(zval *first, zval *second) /* {{{ */
5034 {
5035 	return string_compare_function(first, second);
5036 }
5037 /* }}} */
5038 
5039 static int zval_user_compare(zval *a, zval *b) /* {{{ */
5040 {
5041 	zval args[2];
5042 	zval retval;
5043 
5044 	ZVAL_COPY_VALUE(&args[0], a);
5045 	ZVAL_COPY_VALUE(&args[1], b);
5046 
5047 	BG(user_compare_fci).param_count = 2;
5048 	BG(user_compare_fci).params = args;
5049 	BG(user_compare_fci).retval = &retval;
5050 
5051 	if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
5052 		zend_long ret = zval_get_long(&retval);
5053 		zval_ptr_dtor(&retval);
5054 		return ZEND_NORMALIZE_BOOL(ret);
5055 	} else {
5056 		return 0;
5057 	}
5058 }
5059 /* }}} */
5060 
5061 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5062 {
5063 	uint32_t argc, i;
5064 	zval *args;
5065 	int (*intersect_data_compare_func)(zval *, zval *) = NULL;
5066 	bool ok;
5067 	zval *val, *data;
5068 	char *param_spec;
5069 	zend_string *key;
5070 	zend_ulong h;
5071 
5072 	/* Get the argument count */
5073 	argc = ZEND_NUM_ARGS();
5074 	if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5075 		/* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
5076 		param_spec = "+f";
5077 		intersect_data_compare_func = zval_user_compare;
5078 	} else {
5079 		/* 	INTERSECT_COMP_DATA_NONE - array_intersect_key()
5080 			INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
5081 		param_spec = "+";
5082 
5083 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5084 			intersect_data_compare_func = zval_compare;
5085 		}
5086 	}
5087 
5088 	if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5089 		RETURN_THROWS();
5090 	}
5091 
5092 	for (i = 0; i < argc; i++) {
5093 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5094 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5095 			RETURN_THROWS();
5096 		}
5097 	}
5098 
5099 	array_init(return_value);
5100 
5101 	/* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
5102 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5103 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5104 			val = Z_REFVAL_P(val);
5105 		}
5106 		if (key == NULL) {
5107 			ok = 1;
5108 			for (i = 1; i < argc; i++) {
5109 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
5110 					(intersect_data_compare_func &&
5111 					intersect_data_compare_func(val, data) != 0)
5112 				) {
5113 					ok = 0;
5114 					break;
5115 				}
5116 			}
5117 			if (ok) {
5118 				Z_TRY_ADDREF_P(val);
5119 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5120 			}
5121 		} else {
5122 			ok = 1;
5123 			for (i = 1; i < argc; i++) {
5124 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5125 					(intersect_data_compare_func &&
5126 					intersect_data_compare_func(val, data) != 0)
5127 				) {
5128 					ok = 0;
5129 					break;
5130 				}
5131 			}
5132 			if (ok) {
5133 				Z_TRY_ADDREF_P(val);
5134 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5135 			}
5136 		}
5137 	} ZEND_HASH_FOREACH_END();
5138 }
5139 /* }}} */
5140 
5141 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5142 {
5143 	zval *args = NULL;
5144 	HashTable *hash;
5145 	uint32_t arr_argc, i;
5146 	int c = 0;
5147 	uint32_t idx;
5148 	Bucket **lists, *list, **ptrs, *p;
5149 	char *param_spec;
5150 	zend_fcall_info fci1, fci2;
5151 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5152 	zend_fcall_info *fci_key = NULL, *fci_data;
5153 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5154 	PHP_ARRAY_CMP_FUNC_VARS;
5155 	bool in_place = false;
5156 
5157 	bucket_compare_func_t intersect_key_compare_func;
5158 	bucket_compare_func_t intersect_data_compare_func;
5159 
5160 	if (behavior == INTERSECT_NORMAL) {
5161 		intersect_key_compare_func = php_array_key_compare_string;
5162 
5163 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5164 			/* array_intersect() */
5165 			param_spec = "+";
5166 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5167 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5168 			/* array_uintersect() */
5169 			param_spec = "+f";
5170 			intersect_data_compare_func = php_array_user_compare_unstable;
5171 		} else {
5172 			ZEND_ASSERT(0 && "Invalid data_compare_type");
5173 			return;
5174 		}
5175 
5176 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5177 			RETURN_THROWS();
5178 		}
5179 		fci_data = &fci1;
5180 		fci_data_cache = &fci1_cache;
5181 
5182 	} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5183 		/* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5184 		 * no comparison of the data is done (part of INTERSECT_ASSOC) */
5185 
5186 		if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5187 			/* array_intersect_assoc() or array_intersect_key() */
5188 			param_spec = "+";
5189 			intersect_key_compare_func = php_array_key_compare_string_unstable;
5190 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5191 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5192 			/* array_uintersect_assoc() */
5193 			param_spec = "+f";
5194 			intersect_key_compare_func = php_array_key_compare_string_unstable;
5195 			intersect_data_compare_func = php_array_user_compare_unstable;
5196 			fci_data = &fci1;
5197 			fci_data_cache = &fci1_cache;
5198 		} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5199 			/* array_intersect_uassoc() or array_intersect_ukey() */
5200 			param_spec = "+f";
5201 			intersect_key_compare_func = php_array_user_key_compare_unstable;
5202 			intersect_data_compare_func = php_array_data_compare_string_unstable;
5203 			fci_key = &fci1;
5204 			fci_key_cache = &fci1_cache;
5205 		} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5206 			/* array_uintersect_uassoc() */
5207 			param_spec = "+ff";
5208 			intersect_key_compare_func = php_array_user_key_compare_unstable;
5209 			intersect_data_compare_func = php_array_user_compare_unstable;
5210 			fci_data = &fci1;
5211 			fci_data_cache = &fci1_cache;
5212 			fci_key = &fci2;
5213 			fci_key_cache = &fci2_cache;
5214 		} else {
5215 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5216 			return;
5217 		}
5218 
5219 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5220 			RETURN_THROWS();
5221 		}
5222 
5223 	} else {
5224 		ZEND_ASSERT(0 && "Invalid behavior");
5225 		return;
5226 	}
5227 
5228 	PHP_ARRAY_CMP_FUNC_BACKUP();
5229 
5230 	/* for each argument, create and sort list with pointers to the hash buckets */
5231 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5232 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5233 
5234 	if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5235 		BG(user_compare_fci) = *fci_data;
5236 		BG(user_compare_fci_cache) = *fci_data_cache;
5237 	} else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5238 		BG(user_compare_fci) = *fci_key;
5239 		BG(user_compare_fci_cache) = *fci_key_cache;
5240 	}
5241 
5242 	for (i = 0; i < arr_argc; i++) {
5243 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5244 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5245 			arr_argc = i; /* only free up to i - 1 */
5246 			goto out;
5247 		}
5248 		hash = Z_ARRVAL(args[i]);
5249 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5250 		lists[i] = list;
5251 		ptrs[i] = list;
5252 		if (HT_IS_PACKED(hash)) {
5253 			zval *zv = hash->arPacked;
5254 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5255 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5256 				ZVAL_COPY_VALUE(&list->val, zv);
5257 				list->h = idx;
5258 				list->key = NULL;
5259 				list++;
5260 			}
5261 		} else {
5262 			p = hash->arData;
5263 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5264 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5265 				*list++ = *p;
5266 			}
5267 		}
5268 		ZVAL_UNDEF(&list->val);
5269 		if (hash->nNumOfElements > 1) {
5270 			if (behavior == INTERSECT_NORMAL) {
5271 				zend_sort((void *) lists[i], hash->nNumOfElements,
5272 						sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5273 						(swap_func_t)zend_hash_bucket_swap);
5274 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5275 				zend_sort((void *) lists[i], hash->nNumOfElements,
5276 						sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5277 						(swap_func_t)zend_hash_bucket_swap);
5278 			}
5279 		}
5280 	}
5281 
5282 	/* copy the argument array if necessary */
5283 	in_place = zend_may_modify_arg_in_place(&args[0]);
5284 	if (in_place) {
5285 		RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5286 	} else {
5287 		RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5288 	}
5289 
5290 	/* go through the lists and look for common values */
5291 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5292 		if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5293 			&& key_compare_type == INTERSECT_COMP_KEY_USER) {
5294 			BG(user_compare_fci) = *fci_key;
5295 			BG(user_compare_fci_cache) = *fci_key_cache;
5296 		}
5297 
5298 		for (i = 1; i < arr_argc; i++) {
5299 			if (behavior & INTERSECT_NORMAL) {
5300 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5301 					ptrs[i]++;
5302 				}
5303 			} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5304 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5305 					ptrs[i]++;
5306 				}
5307 				if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5308 					/* this means that ptrs[i] is not NULL so we can compare
5309 					 * and "c==0" is from last operation
5310 					 * in this branch of code we enter only when INTERSECT_ASSOC
5311 					 * since when we have INTERSECT_KEY compare of data is not wanted. */
5312 					if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5313 						BG(user_compare_fci) = *fci_data;
5314 						BG(user_compare_fci_cache) = *fci_data_cache;
5315 					}
5316 					if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5317 						c = 1;
5318 						if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5319 							BG(user_compare_fci) = *fci_key;
5320 							BG(user_compare_fci_cache) = *fci_key_cache;
5321 							/* When KEY_USER, the last parameter is always the callback */
5322 						}
5323 						/* we are going to the break */
5324 					} else {
5325 						/* continue looping */
5326 					}
5327 				}
5328 			}
5329 			if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5330 				/* delete any values corresponding to remains of ptrs[0] */
5331 				/* and exit because they do not present in at least one of */
5332 				/* the other arguments */
5333 				for (;;) {
5334 					p = ptrs[0]++;
5335 					if (Z_TYPE(p->val) == IS_UNDEF) {
5336 						goto out;
5337 					}
5338 					if (p->key == NULL) {
5339 						zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5340 					} else {
5341 						zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5342 					}
5343 				}
5344 			}
5345 			if (c) /* here we get if not all are equal */
5346 				break;
5347 			ptrs[i]++;
5348 		}
5349 		if (c) {
5350 			/* Value of ptrs[0] not in all arguments, delete all entries */
5351 			/* with value < value of ptrs[i] */
5352 			for (;;) {
5353 				p = ptrs[0];
5354 				if (p->key == NULL) {
5355 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5356 				} else {
5357 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5358 				}
5359 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5360 					goto out;
5361 				}
5362 				if (behavior == INTERSECT_NORMAL) {
5363 					if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5364 						break;
5365 					}
5366 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5367 					/* no need of looping because indexes are unique */
5368 					break;
5369 				}
5370 			}
5371 		} else {
5372 			/* ptrs[0] is present in all the arguments */
5373 			/* Skip all entries with same value as ptrs[0] */
5374 			for (;;) {
5375 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5376 					goto out;
5377 				}
5378 				if (behavior == INTERSECT_NORMAL) {
5379 					if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5380 						break;
5381 					}
5382 				} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5383 					/* no need of looping because indexes are unique */
5384 					break;
5385 				}
5386 			}
5387 		}
5388 	}
5389 out:
5390 	for (i = 0; i < arr_argc; i++) {
5391 		hash = Z_ARRVAL(args[i]);
5392 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5393 	}
5394 
5395 	PHP_ARRAY_CMP_FUNC_RESTORE();
5396 
5397 	efree(ptrs);
5398 	efree(lists);
5399 
5400 	if (in_place) {
5401 		Z_ADDREF_P(return_value);
5402 	}
5403 }
5404 /* }}} */
5405 
5406 /* {{{ 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. */
5407 PHP_FUNCTION(array_intersect_key)
5408 {
5409 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5410 }
5411 /* }}} */
5412 
5413 /* {{{ 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. */
5414 PHP_FUNCTION(array_intersect_ukey)
5415 {
5416 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5417 }
5418 /* }}} */
5419 
5420 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5421 PHP_FUNCTION(array_intersect)
5422 {
5423 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5424 }
5425 /* }}} */
5426 
5427 /* {{{ 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. */
5428 PHP_FUNCTION(array_uintersect)
5429 {
5430 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5431 }
5432 /* }}} */
5433 
5434 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5435 PHP_FUNCTION(array_intersect_assoc)
5436 {
5437 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5438 }
5439 /* }}} */
5440 
5441 /* {{{ 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. */
5442 PHP_FUNCTION(array_intersect_uassoc)
5443 {
5444 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5445 }
5446 /* }}} */
5447 
5448 /* {{{ 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. */
5449 PHP_FUNCTION(array_uintersect_assoc)
5450 {
5451 	php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5452 }
5453 /* }}} */
5454 
5455 /* {{{ 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. */
5456 PHP_FUNCTION(array_uintersect_uassoc)
5457 {
5458 	php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5459 }
5460 /* }}} */
5461 
5462 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5463 {
5464 	uint32_t argc, i;
5465 	zval *args;
5466 	int (*diff_data_compare_func)(zval *, zval *) = NULL;
5467 	bool ok;
5468 	zval *val, *data;
5469 	zend_string *key;
5470 	zend_ulong h;
5471 
5472 	/* Get the argument count */
5473 	argc = ZEND_NUM_ARGS();
5474 	if (data_compare_type == DIFF_COMP_DATA_USER) {
5475 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5476 			RETURN_THROWS();
5477 		}
5478 		diff_data_compare_func = zval_user_compare;
5479 	} else {
5480 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5481 			RETURN_THROWS();
5482 		}
5483 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5484 			diff_data_compare_func = zval_compare;
5485 		}
5486 	}
5487 
5488 	for (i = 0; i < argc; i++) {
5489 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5490 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5491 			RETURN_THROWS();
5492 		}
5493 	}
5494 
5495 	array_init(return_value);
5496 
5497 	/* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5498 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5499 		if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5500 			val = Z_REFVAL_P(val);
5501 		}
5502 		if (key == NULL) {
5503 			ok = 1;
5504 			for (i = 1; i < argc; i++) {
5505 				if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5506 					(!diff_data_compare_func ||
5507 					diff_data_compare_func(val, data) == 0)
5508 				) {
5509 					ok = 0;
5510 					break;
5511 				}
5512 			}
5513 			if (ok) {
5514 				Z_TRY_ADDREF_P(val);
5515 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5516 			}
5517 		} else {
5518 			ok = 1;
5519 			for (i = 1; i < argc; i++) {
5520 				if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5521 					(!diff_data_compare_func ||
5522 					diff_data_compare_func(val, data) == 0)
5523 				) {
5524 					ok = 0;
5525 					break;
5526 				}
5527 			}
5528 			if (ok) {
5529 				Z_TRY_ADDREF_P(val);
5530 				zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5531 			}
5532 		}
5533 	} ZEND_HASH_FOREACH_END();
5534 }
5535 /* }}} */
5536 
5537 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5538 {
5539 	zval *args = NULL;
5540 	HashTable *hash;
5541 	uint32_t arr_argc, i;
5542 	int c;
5543 	uint32_t idx;
5544 	Bucket **lists, *list, **ptrs, *p;
5545 	char *param_spec;
5546 	zend_fcall_info fci1, fci2;
5547 	zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5548 	zend_fcall_info *fci_key = NULL, *fci_data;
5549 	zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5550 	PHP_ARRAY_CMP_FUNC_VARS;
5551 
5552 	bucket_compare_func_t diff_key_compare_func;
5553 	bucket_compare_func_t diff_data_compare_func;
5554 
5555 	if (behavior == DIFF_NORMAL) {
5556 		diff_key_compare_func = php_array_key_compare_string_unstable;
5557 
5558 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5559 			/* array_diff */
5560 			param_spec = "+";
5561 			diff_data_compare_func = php_array_data_compare_string_unstable;
5562 		} else if (data_compare_type == DIFF_COMP_DATA_USER) {
5563 			/* array_udiff */
5564 			param_spec = "+f";
5565 			diff_data_compare_func = php_array_user_compare_unstable;
5566 		} else {
5567 			ZEND_ASSERT(0 && "Invalid data_compare_type");
5568 			return;
5569 		}
5570 
5571 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5572 			RETURN_THROWS();
5573 		}
5574 		fci_data = &fci1;
5575 		fci_data_cache = &fci1_cache;
5576 
5577 	} else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5578 		/* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5579 		 * no comparison of the data is done (part of DIFF_ASSOC) */
5580 
5581 		if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5582 			/* array_diff_assoc() or array_diff_key() */
5583 			param_spec = "+";
5584 			diff_key_compare_func = php_array_key_compare_string_unstable;
5585 			diff_data_compare_func = php_array_data_compare_string_unstable;
5586 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5587 			/* array_udiff_assoc() */
5588 			param_spec = "+f";
5589 			diff_key_compare_func = php_array_key_compare_string_unstable;
5590 			diff_data_compare_func = php_array_user_compare_unstable;
5591 			fci_data = &fci1;
5592 			fci_data_cache = &fci1_cache;
5593 		} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5594 			/* array_diff_uassoc() or array_diff_ukey() */
5595 			param_spec = "+f";
5596 			diff_key_compare_func = php_array_user_key_compare_unstable;
5597 			diff_data_compare_func = php_array_data_compare_string_unstable;
5598 			fci_key = &fci1;
5599 			fci_key_cache = &fci1_cache;
5600 		} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5601 			/* array_udiff_uassoc() */
5602 			param_spec = "+ff";
5603 			diff_key_compare_func = php_array_user_key_compare_unstable;
5604 			diff_data_compare_func = php_array_user_compare_unstable;
5605 			fci_data = &fci1;
5606 			fci_data_cache = &fci1_cache;
5607 			fci_key = &fci2;
5608 			fci_key_cache = &fci2_cache;
5609 		} else {
5610 			ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5611 			return;
5612 		}
5613 
5614 		if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5615 			RETURN_THROWS();
5616 		}
5617 
5618 	} else {
5619 		ZEND_ASSERT(0 && "Invalid behavior");
5620 		return;
5621 	}
5622 
5623 	PHP_ARRAY_CMP_FUNC_BACKUP();
5624 
5625 	/* for each argument, create and sort list with pointers to the hash buckets */
5626 	lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5627 	ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5628 
5629 	if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5630 		BG(user_compare_fci) = *fci_data;
5631 		BG(user_compare_fci_cache) = *fci_data_cache;
5632 	} else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5633 		BG(user_compare_fci) = *fci_key;
5634 		BG(user_compare_fci_cache) = *fci_key_cache;
5635 	}
5636 
5637 	for (i = 0; i < arr_argc; i++) {
5638 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5639 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5640 			arr_argc = i; /* only free up to i - 1 */
5641 			goto out;
5642 		}
5643 		hash = Z_ARRVAL(args[i]);
5644 		list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5645 		lists[i] = list;
5646 		ptrs[i] = list;
5647 		if (HT_IS_PACKED(hash)) {
5648 			zval *zv = hash->arPacked;
5649 			for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5650 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5651 				ZVAL_COPY_VALUE(&list->val, zv);
5652 				list->h = idx;
5653 				list->key = NULL;
5654 				list++;
5655 			}
5656 		} else {
5657 			p = hash->arData;
5658 			for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5659 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
5660 				*list++ = *p;
5661 			}
5662 		}
5663 		ZVAL_UNDEF(&list->val);
5664 		if (hash->nNumOfElements > 1) {
5665 			if (behavior == DIFF_NORMAL) {
5666 				zend_sort((void *) lists[i], hash->nNumOfElements,
5667 						sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5668 						(swap_func_t)zend_hash_bucket_swap);
5669 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5670 				zend_sort((void *) lists[i], hash->nNumOfElements,
5671 						sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5672 						(swap_func_t)zend_hash_bucket_swap);
5673 			}
5674 		}
5675 	}
5676 
5677 	/* copy the argument array */
5678 	RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5679 
5680 	/* go through the lists and look for values of ptr[0] that are not in the others */
5681 	while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5682 		if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5683 			&&
5684 			key_compare_type == DIFF_COMP_KEY_USER
5685 		) {
5686 			BG(user_compare_fci) = *fci_key;
5687 			BG(user_compare_fci_cache) = *fci_key_cache;
5688 		}
5689 		c = 1;
5690 		for (i = 1; i < arr_argc; i++) {
5691 			Bucket *ptr = ptrs[i];
5692 			if (behavior == DIFF_NORMAL) {
5693 				while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5694 					ptrs[i]++;
5695 				}
5696 			} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5697 				while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5698 					ptr++;
5699 				}
5700 			}
5701 			if (!c) {
5702 				if (behavior == DIFF_NORMAL) {
5703 					if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5704 						ptrs[i]++;
5705 					}
5706 					break;
5707 				} else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
5708 					/* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5709 					 * data comparison is not needed - skipped. */
5710 					if (Z_TYPE(ptr->val) != IS_UNDEF) {
5711 						if (data_compare_type == DIFF_COMP_DATA_USER) {
5712 							BG(user_compare_fci) = *fci_data;
5713 							BG(user_compare_fci_cache) = *fci_data_cache;
5714 						}
5715 						if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5716 							/* the data is not the same */
5717 							c = -1;
5718 							if (key_compare_type == DIFF_COMP_KEY_USER) {
5719 								BG(user_compare_fci) = *fci_key;
5720 								BG(user_compare_fci_cache) = *fci_key_cache;
5721 							}
5722 						} else {
5723 							break;
5724 							/* we have found the element in other arrays thus we don't want it
5725 							 * in the return_value -> delete from there */
5726 						}
5727 					}
5728 				} else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5729 					/* the behavior here differs from INTERSECT_KEY in php_intersect
5730 					 * since in the "diff" case we have to remove the entry from
5731 					 * return_value while when doing intersection the entry must not
5732 					 * be deleted. */
5733 					break; /* remove the key */
5734 				}
5735 			}
5736 		}
5737 		if (!c) {
5738 			/* ptrs[0] in one of the other arguments */
5739 			/* delete all entries with value as ptrs[0] */
5740 			for (;;) {
5741 				p = ptrs[0];
5742 				if (p->key == NULL) {
5743 					zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5744 				} else {
5745 					zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5746 				}
5747 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5748 					goto out;
5749 				}
5750 				if (behavior == DIFF_NORMAL) {
5751 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5752 						break;
5753 					}
5754 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5755 					/* in this case no array_key_compare is needed */
5756 					break;
5757 				}
5758 			}
5759 		} else {
5760 			/* ptrs[0] in none of the other arguments */
5761 			/* skip all entries with value as ptrs[0] */
5762 			for (;;) {
5763 				if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5764 					goto out;
5765 				}
5766 				if (behavior == DIFF_NORMAL) {
5767 					if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5768 						break;
5769 					}
5770 				} else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5771 					/* in this case no array_key_compare is needed */
5772 					break;
5773 				}
5774 			}
5775 		}
5776 	}
5777 out:
5778 	for (i = 0; i < arr_argc; i++) {
5779 		hash = Z_ARRVAL(args[i]);
5780 		pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5781 	}
5782 
5783 	PHP_ARRAY_CMP_FUNC_RESTORE();
5784 
5785 	efree(ptrs);
5786 	efree(lists);
5787 }
5788 /* }}} */
5789 
5790 /* {{{ 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. */
5791 PHP_FUNCTION(array_diff_key)
5792 {
5793 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5794 }
5795 /* }}} */
5796 
5797 /* {{{ 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. */
5798 PHP_FUNCTION(array_diff_ukey)
5799 {
5800 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5801 }
5802 /* }}} */
5803 
5804 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5805 PHP_FUNCTION(array_diff)
5806 {
5807 	zval *args;
5808 	uint32_t argc, i;
5809 	uint32_t num;
5810 	HashTable exclude;
5811 	zval *value;
5812 	zend_string *str, *tmp_str, *key;
5813 	zend_long idx;
5814 	zval dummy;
5815 
5816 	ZEND_PARSE_PARAMETERS_START(1, -1)
5817 		Z_PARAM_VARIADIC('+', args, argc)
5818 	ZEND_PARSE_PARAMETERS_END();
5819 
5820 	if (Z_TYPE(args[0]) != IS_ARRAY) {
5821 		zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5822 		RETURN_THROWS();
5823 	}
5824 
5825 	num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5826 	if (num == 0) {
5827 		for (i = 1; i < argc; i++) {
5828 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5829 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5830 				RETURN_THROWS();
5831 			}
5832 		}
5833 		RETURN_EMPTY_ARRAY();
5834 	} else if (num == 1) {
5835 		int found = 0;
5836 		zend_string *search_str, *tmp_search_str;
5837 
5838 		value = NULL;
5839 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5840 			break;
5841 		} ZEND_HASH_FOREACH_END();
5842 
5843 		if (!value) {
5844 			for (i = 1; i < argc; i++) {
5845 				if (Z_TYPE(args[i]) != IS_ARRAY) {
5846 					zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5847 					RETURN_THROWS();
5848 				}
5849 			}
5850 			RETURN_EMPTY_ARRAY();
5851 		}
5852 
5853 		search_str = zval_get_tmp_string(value, &tmp_search_str);
5854 
5855 		for (i = 1; i < argc; i++) {
5856 			if (Z_TYPE(args[i]) != IS_ARRAY) {
5857 				zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5858 				RETURN_THROWS();
5859 			}
5860 			if (!found) {
5861 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5862 					str = zval_get_tmp_string(value, &tmp_str);
5863 					if (zend_string_equals(search_str, str)) {
5864 						zend_tmp_string_release(tmp_str);
5865 						found = 1;
5866 						break;
5867 					}
5868 					zend_tmp_string_release(tmp_str);
5869 				} ZEND_HASH_FOREACH_END();
5870 			}
5871 		}
5872 
5873 		zend_tmp_string_release(tmp_search_str);
5874 
5875 		if (found) {
5876 			RETVAL_EMPTY_ARRAY();
5877 		} else {
5878 			ZVAL_COPY(return_value, &args[0]);
5879 		}
5880 		return;
5881 	}
5882 
5883 	/* count number of elements */
5884 	num = 0;
5885 	for (i = 1; i < argc; i++) {
5886 		if (Z_TYPE(args[i]) != IS_ARRAY) {
5887 			zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5888 			RETURN_THROWS();
5889 		}
5890 		num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5891 	}
5892 
5893 	if (num == 0) {
5894 		ZVAL_COPY(return_value, &args[0]);
5895 		return;
5896 	}
5897 
5898 	ZVAL_NULL(&dummy);
5899 	/* create exclude map */
5900 	zend_hash_init(&exclude, num, NULL, NULL, 0);
5901 	for (i = 1; i < argc; i++) {
5902 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5903 			str = zval_get_tmp_string(value, &tmp_str);
5904 			zend_hash_add(&exclude, str, &dummy);
5905 			zend_tmp_string_release(tmp_str);
5906 		} ZEND_HASH_FOREACH_END();
5907 	}
5908 
5909 	/* copy all elements of first array that are not in exclude set */
5910 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5911 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5912 		str = zval_get_tmp_string(value, &tmp_str);
5913 		if (!zend_hash_exists(&exclude, str)) {
5914 			if (key) {
5915 				value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5916 			} else {
5917 				value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5918 			}
5919 			zval_add_ref(value);
5920 		}
5921 		zend_tmp_string_release(tmp_str);
5922 	} ZEND_HASH_FOREACH_END();
5923 
5924 	zend_hash_destroy(&exclude);
5925 }
5926 /* }}} */
5927 
5928 /* {{{ 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. */
5929 PHP_FUNCTION(array_udiff)
5930 {
5931 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5932 }
5933 /* }}} */
5934 
5935 /* {{{ 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 */
5936 PHP_FUNCTION(array_diff_assoc)
5937 {
5938 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5939 }
5940 /* }}} */
5941 
5942 /* {{{ 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. */
5943 PHP_FUNCTION(array_diff_uassoc)
5944 {
5945 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5946 }
5947 /* }}} */
5948 
5949 /* {{{ 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. */
5950 PHP_FUNCTION(array_udiff_assoc)
5951 {
5952 	php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5953 }
5954 /* }}} */
5955 
5956 /* {{{ 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. */
5957 PHP_FUNCTION(array_udiff_uassoc)
5958 {
5959 	php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5960 }
5961 /* }}} */
5962 
5963 #define MULTISORT_ORDER	0
5964 #define MULTISORT_TYPE	1
5965 #define MULTISORT_LAST	2
5966 
5967 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5968 {
5969 	Bucket *ab = *(Bucket **)a;
5970 	Bucket *bb = *(Bucket **)b;
5971 	int r;
5972 	zend_long result;
5973 
5974 	r = 0;
5975 	do {
5976 		result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5977 		if (result != 0) {
5978 			return result > 0 ? 1 : -1;
5979 		}
5980 		r++;
5981 	} while (Z_TYPE(ab[r].val) != IS_UNDEF);
5982 
5983 	return stable_sort_fallback(&ab[r], &bb[r]);
5984 }
5985 /* }}} */
5986 
5987 #define MULTISORT_ABORT				\
5988 	efree(func);	\
5989 	efree(arrays);					\
5990 	return;
5991 
5992 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5993 	Bucket *t;
5994 	Bucket **f = (Bucket **)p;
5995 	Bucket **g = (Bucket **)q;
5996 
5997 	t = *f;
5998 	*f = *g;
5999 	*g = t;
6000 }
6001 /* }}} */
6002 
6003 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
6004 PHP_FUNCTION(array_multisort)
6005 {
6006 	zval*			args;
6007 	zval**			arrays;
6008 	Bucket**		indirect;
6009 	uint32_t		idx;
6010 	HashTable*		hash;
6011 	uint32_t		argc;
6012 	uint32_t		array_size;
6013 	uint32_t		num_arrays = 0;
6014 	int				parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
6015 	int				sort_order = PHP_SORT_ASC;
6016 	int				sort_type  = PHP_SORT_REGULAR;
6017 	uint32_t		i, k, n;
6018 	bucket_compare_func_t *func;
6019 
6020 	ZEND_PARSE_PARAMETERS_START(1, -1)
6021 		Z_PARAM_VARIADIC('+', args, argc)
6022 	ZEND_PARSE_PARAMETERS_END();
6023 
6024 	/* Allocate space for storing pointers to input arrays and sort flags. */
6025 	arrays = (zval **)ecalloc(argc, sizeof(zval *));
6026 	for (i = 0; i < MULTISORT_LAST; i++) {
6027 		parse_state[i] = 0;
6028 	}
6029 	func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
6030 
6031 	/* Here we go through the input arguments and parse them. Each one can
6032 	 * be either an array or a sort flag which follows an array. If not
6033 	 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
6034 	 * accordingly. There can't be two sort flags of the same type after an
6035 	 * array, and the very first argument has to be an array. */
6036 	for (i = 0; i < argc; i++) {
6037 		zval *arg = &args[i];
6038 
6039 		ZVAL_DEREF(arg);
6040 		if (Z_TYPE_P(arg) == IS_ARRAY) {
6041 			SEPARATE_ARRAY(arg);
6042 			/* We see the next array, so we update the sort flags of
6043 			 * the previous array and reset the sort flags. */
6044 			if (i > 0) {
6045 				ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6046 				sort_order = PHP_SORT_ASC;
6047 				sort_type = PHP_SORT_REGULAR;
6048 			}
6049 			arrays[num_arrays++] = arg;
6050 
6051 			/* Next one may be an array or a list of sort flags. */
6052 			for (k = 0; k < MULTISORT_LAST; k++) {
6053 				parse_state[k] = 1;
6054 			}
6055 		} else if (Z_TYPE_P(arg) == IS_LONG) {
6056 			switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
6057 				case PHP_SORT_ASC:
6058 				case PHP_SORT_DESC:
6059 					/* flag allowed here */
6060 					if (parse_state[MULTISORT_ORDER] == 1) {
6061 						/* Save the flag and make sure then next arg is not the current flag. */
6062 						sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
6063 						parse_state[MULTISORT_ORDER] = 0;
6064 					} else {
6065 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6066 						MULTISORT_ABORT;
6067 					}
6068 					break;
6069 
6070 				case PHP_SORT_REGULAR:
6071 				case PHP_SORT_NUMERIC:
6072 				case PHP_SORT_STRING:
6073 				case PHP_SORT_NATURAL:
6074 				case PHP_SORT_LOCALE_STRING:
6075 					/* flag allowed here */
6076 					if (parse_state[MULTISORT_TYPE] == 1) {
6077 						/* Save the flag and make sure then next arg is not the current flag. */
6078 						sort_type = (int)Z_LVAL_P(arg);
6079 						parse_state[MULTISORT_TYPE] = 0;
6080 					} else {
6081 						zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6082 						MULTISORT_ABORT;
6083 					}
6084 					break;
6085 
6086 				default:
6087 					zend_argument_value_error(i + 1, "must be a valid sort flag");
6088 					MULTISORT_ABORT;
6089 					break;
6090 
6091 			}
6092 		} else {
6093 			zend_argument_type_error(i + 1, "must be an array or a sort flag");
6094 			MULTISORT_ABORT;
6095 		}
6096 	}
6097 	/* Take care of the last array sort flags. */
6098 	ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6099 
6100 	/* Make sure the arrays are of the same size. */
6101 	array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6102 	for (i = 1; i < num_arrays; i++) {
6103 		if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
6104 			zend_value_error("Array sizes are inconsistent");
6105 			MULTISORT_ABORT;
6106 		}
6107 	}
6108 
6109 	/* If all arrays are empty we don't need to do anything. */
6110 	if (array_size < 1) {
6111 		efree(func);
6112 		efree(arrays);
6113 		RETURN_TRUE;
6114 	}
6115 
6116 	/* Create the indirection array. This array is of size MxN, where
6117 	 * M is the number of entries in each input array and N is the number
6118 	 * of the input arrays + 1. The last column is UNDEF to indicate the end
6119 	 * of the row. It also stores the original position for stable sorting. */
6120 	indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6121 	/* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6122 	Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6123 	for (i = 0; i < array_size; i++) {
6124 		indirect[i] = indirects + (i * (num_arrays + 1));
6125 	}
6126 	for (i = 0; i < num_arrays; i++) {
6127 		k = 0;
6128 		if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6129 			zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6130 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6131 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6132 				ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6133 				indirect[k][i].h = idx;
6134 				indirect[k][i].key = NULL;
6135 				k++;
6136 			}
6137 		} else {
6138 			Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6139 			for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6140 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
6141 				indirect[k][i] = *p;
6142 				k++;
6143 			}
6144 		}
6145 	}
6146 	for (k = 0; k < array_size; k++) {
6147 		ZVAL_UNDEF(&indirect[k][num_arrays].val);
6148 		Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6149 	}
6150 
6151 	/* Do the actual sort magic - bada-bim, bada-boom. */
6152 	zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6153 	if (EG(exception)) {
6154 		goto clean_up;
6155 	}
6156 
6157 	/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6158 	for (i = 0; i < num_arrays; i++) {
6159 		hash = Z_ARRVAL_P(arrays[i]);
6160 		hash->nNumUsed = array_size;
6161 		hash->nNextFreeElement = array_size;
6162 		hash->nInternalPointer = 0;
6163 		if (HT_IS_PACKED(hash)) {
6164 			for (k = 0; k < array_size; k++) {
6165 				ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6166 			}
6167 		} else {
6168 			bool repack = true;
6169 
6170 			for (n = 0, k = 0; k < array_size; k++) {
6171 				hash->arData[k] = indirect[k][i];
6172 				if (hash->arData[k].key == NULL) {
6173 					hash->arData[k].h = n++;
6174 				} else {
6175 					repack = false;
6176 				}
6177 			}
6178 			if (repack) {
6179 				zend_hash_to_packed(hash);
6180 			} else {
6181 				zend_hash_rehash(hash);
6182 			}
6183 		}
6184 	}
6185 	RETVAL_TRUE;
6186 
6187 clean_up:
6188 	efree(indirects);
6189 	efree(indirect);
6190 	efree(func);
6191 	efree(arrays);
6192 }
6193 /* }}} */
6194 
6195 /* {{{ php_array_pick_keys */
6196 PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent)
6197 {
6198 	const php_random_algo *algo = engine.algo;
6199 	void *state = engine.state;
6200 
6201 	HashTable *ht = Z_ARRVAL_P(input);
6202 	uint32_t num_avail = zend_hash_num_elements(ht);
6203 	zend_long i, randval;
6204 	zend_string *string_key;
6205 	zend_ulong num_key;
6206 	zval *zv;
6207 	Bucket *b;
6208 	zend_bitset bitset;
6209 	int negative_bitset = 0;
6210 	uint32_t bitset_len;
6211 	ALLOCA_FLAG(use_heap);
6212 
6213 	if (num_avail == 0) {
6214 		if (!silent) {
6215 			zend_argument_must_not_be_empty_error(1);
6216 		}
6217 		return false;
6218 	}
6219 
6220 	if (num_req == 1) {
6221 		if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6222 			/* If less than 1/2 of elements are used, don't sample. Instead search for a
6223 			 * specific offset using linear scan. */
6224 			i = 0;
6225 			randval = algo->range(state, 0, num_avail - 1);
6226 			if (EG(exception)) {
6227 				return false;
6228 			}
6229 			ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6230 				if (i == randval) {
6231 					if (string_key) {
6232 						ZVAL_STR_COPY(retval, string_key);
6233 					} else {
6234 						ZVAL_LONG(retval, num_key);
6235 					}
6236 					return true;
6237 				}
6238 				i++;
6239 			} ZEND_HASH_FOREACH_END();
6240 		}
6241 
6242 		/* Sample random buckets until we hit one that is not empty.
6243 		 * The worst case probability of hitting an empty element is 1-1/2. The worst case
6244 		 * probability of hitting N empty elements in a row is (1-1/2)**N.
6245 		 * For N=10 this becomes smaller than 0.1%. */
6246 		if (HT_IS_PACKED(ht)) {
6247 			do {
6248 				randval = algo->range(state, 0, ht->nNumUsed - 1);
6249 				if (EG(exception)) {
6250 					return false;
6251 				}
6252 				zv = &ht->arPacked[randval];
6253 				if (!Z_ISUNDEF_P(zv)) {
6254 					ZVAL_LONG(retval, randval);
6255 					return true;
6256 				}
6257 			} while (true);
6258 		} else {
6259 			do {
6260 				randval = algo->range(state, 0, ht->nNumUsed - 1);
6261 				if (EG(exception)) {
6262 					return false;
6263 				}
6264 				b = &ht->arData[randval];
6265 				if (!Z_ISUNDEF(b->val)) {
6266 					if (b->key) {
6267 						ZVAL_STR_COPY(retval, b->key);
6268 					} else {
6269 						ZVAL_LONG(retval, b->h);
6270 					}
6271 					return true;
6272 				}
6273 			} while (true);
6274 		}
6275 	}
6276 
6277 	if (num_req <= 0 || num_req > num_avail) {
6278 		if (!silent) {
6279 			zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6280 		}
6281 		return false;
6282 	}
6283 
6284 	/* Make the return value an array only if we need to pass back more than one result. */
6285 	array_init_size(retval, (uint32_t) num_req);
6286 	if (num_req > (num_avail >> 1)) {
6287 		negative_bitset = 1;
6288 		num_req = num_avail - num_req;
6289 	}
6290 
6291 	bitset_len = zend_bitset_len(num_avail);
6292 	bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6293 	zend_bitset_clear(bitset, bitset_len);
6294 
6295 	i = num_req;
6296 	int failures = 0;
6297 	while (i) {
6298 		randval = algo->range(state, 0, num_avail - 1);
6299 		if (EG(exception)) {
6300 			goto fail;
6301 		}
6302 		if (zend_bitset_in(bitset, randval)) {
6303 			if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6304 				if (!silent) {
6305 					zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6306 				}
6307 
6308 				goto fail;
6309 			}
6310 		} else {
6311 			zend_bitset_incl(bitset, randval);
6312 			i--;
6313 			failures = 0;
6314 		}
6315 	}
6316 
6317 	zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6318 	ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6319 		/* We can't use zend_hash_index_find()
6320 		 * because the array may have string keys or gaps. */
6321 		ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6322 			if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6323 				if (string_key) {
6324 					ZEND_HASH_FILL_SET_STR_COPY(string_key);
6325 				} else {
6326 					ZEND_HASH_FILL_SET_LONG(num_key);
6327 				}
6328 				ZEND_HASH_FILL_NEXT();
6329 			}
6330 			i++;
6331 		} ZEND_HASH_FOREACH_END();
6332 	} ZEND_HASH_FILL_END();
6333 
6334 	free_alloca(bitset, use_heap);
6335 
6336 	return true;
6337 
6338  fail:
6339 	free_alloca(bitset, use_heap);
6340 
6341 	return false;
6342 }
6343 /* }}} */
6344 
6345 /* {{{ Return key/keys for random entry/entries in the array */
6346 PHP_FUNCTION(array_rand)
6347 {
6348 	zval *input;
6349 	zend_long num_req = 1;
6350 
6351 	ZEND_PARSE_PARAMETERS_START(1, 2)
6352 		Z_PARAM_ARRAY(input)
6353 		Z_PARAM_OPTIONAL
6354 		Z_PARAM_LONG(num_req)
6355 	ZEND_PARSE_PARAMETERS_END();
6356 
6357 	if (!php_array_pick_keys(
6358 			php_random_default_engine(),
6359 			input,
6360 			num_req,
6361 			return_value,
6362 			false)
6363 	) {
6364 		RETURN_THROWS();
6365 	}
6366 }
6367 /* }}} */
6368 
6369 /* Wrapper for array_sum and array_product */
6370 static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6371 {
6372 	HashTable *input;
6373 	zval *entry;
6374 
6375 	ZEND_PARSE_PARAMETERS_START(1, 1)
6376 		Z_PARAM_ARRAY_HT(input)
6377 	ZEND_PARSE_PARAMETERS_END();
6378 
6379 	if (zend_hash_num_elements(input) == 0) {
6380 		RETURN_LONG(initial);
6381 	}
6382 
6383 	ZVAL_LONG(return_value, initial);
6384 	ZEND_HASH_FOREACH_VAL(input, entry) {
6385 		/* For objects we try to cast them to a numeric type */
6386 		if (Z_TYPE_P(entry) == IS_OBJECT) {
6387 			zval dst;
6388 			zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6389 
6390 			/* Do not type error for BC */
6391 			if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6392 				php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6393 					op_name, zend_zval_type_name(entry));
6394 				continue;
6395 			}
6396 			op(return_value, return_value, &dst);
6397 			continue;
6398 		}
6399 
6400 		zend_result status = op(return_value, return_value, entry);
6401 		if (status == FAILURE) {
6402 			ZEND_ASSERT(EG(exception));
6403 			zend_clear_exception();
6404 			/* BC resources: previously resources were cast to int */
6405 			if (Z_TYPE_P(entry) == IS_RESOURCE) {
6406 				zval tmp;
6407 				ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6408 				op(return_value, return_value, &tmp);
6409 			}
6410 			/* BC non numeric strings: previously were cast to 0 */
6411 			else if (Z_TYPE_P(entry) == IS_STRING) {
6412 				zval tmp;
6413 				ZVAL_LONG(&tmp, 0);
6414 				op(return_value, return_value, &tmp);
6415 			}
6416 			php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6417 				op_name, zend_zval_type_name(entry));
6418 		}
6419 	} ZEND_HASH_FOREACH_END();
6420 }
6421 
6422 /* {{{ Returns the sum of the array entries */
6423 PHP_FUNCTION(array_sum)
6424 {
6425 	php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6426 }
6427 /* }}} */
6428 
6429 /* {{{ Returns the product of the array entries */
6430 PHP_FUNCTION(array_product)
6431 {
6432 	php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6433 }
6434 /* }}} */
6435 
6436 /* {{{ Iteratively reduce the array to a single value via the callback. */
6437 PHP_FUNCTION(array_reduce)
6438 {
6439 	zval *input;
6440 	zval args[2];
6441 	zval *operand;
6442 	zval retval;
6443 	zend_fcall_info fci;
6444 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6445 	zval *initial = NULL;
6446 	HashTable *htbl;
6447 
6448 	ZEND_PARSE_PARAMETERS_START(2, 3)
6449 		Z_PARAM_ARRAY(input)
6450 		Z_PARAM_FUNC(fci, fci_cache)
6451 		Z_PARAM_OPTIONAL
6452 		Z_PARAM_ZVAL(initial)
6453 	ZEND_PARSE_PARAMETERS_END();
6454 
6455 
6456 	if (ZEND_NUM_ARGS() > 2) {
6457 		ZVAL_COPY(return_value, initial);
6458 	} else {
6459 		ZVAL_NULL(return_value);
6460 	}
6461 
6462 	/* (zval **)input points to an element of argument stack
6463 	 * the base pointer of which is subject to change.
6464 	 * thus we need to keep the pointer to the hashtable for safety */
6465 	htbl = Z_ARRVAL_P(input);
6466 
6467 	if (zend_hash_num_elements(htbl) == 0) {
6468 		return;
6469 	}
6470 
6471 	fci.retval = &retval;
6472 	fci.param_count = 2;
6473 
6474 	ZEND_HASH_FOREACH_VAL(htbl, operand) {
6475 		ZVAL_COPY_VALUE(&args[0], return_value);
6476 		ZVAL_COPY(&args[1], operand);
6477 		fci.params = args;
6478 
6479 		if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
6480 			zval_ptr_dtor(&args[1]);
6481 			zval_ptr_dtor(&args[0]);
6482 			ZVAL_COPY_VALUE(return_value, &retval);
6483 			if (UNEXPECTED(Z_ISREF_P(return_value))) {
6484 				zend_unwrap_reference(return_value);
6485 			}
6486 		} else {
6487 			zval_ptr_dtor(&args[1]);
6488 			zval_ptr_dtor(&args[0]);
6489 			RETURN_NULL();
6490 		}
6491 	} ZEND_HASH_FOREACH_END();
6492 }
6493 /* }}} */
6494 
6495 /* {{{ Filters elements from the array via the callback. */
6496 PHP_FUNCTION(array_filter)
6497 {
6498 	zval *array;
6499 	zval *operand;
6500 	zval *key;
6501 	zval args[2];
6502 	zval retval;
6503 	bool have_callback = 0;
6504 	zend_long use_type = 0;
6505 	zend_string *string_key;
6506 	zend_fcall_info fci = empty_fcall_info;
6507 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6508 	zend_ulong num_key;
6509 
6510 	ZEND_PARSE_PARAMETERS_START(1, 3)
6511 		Z_PARAM_ARRAY(array)
6512 		Z_PARAM_OPTIONAL
6513 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6514 		Z_PARAM_LONG(use_type)
6515 	ZEND_PARSE_PARAMETERS_END();
6516 
6517 	if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6518 		RETVAL_EMPTY_ARRAY();
6519 		return;
6520 	}
6521 	array_init(return_value);
6522 
6523 	if (ZEND_FCI_INITIALIZED(fci)) {
6524 		have_callback = 1;
6525 		fci.retval = &retval;
6526 		if (use_type == ARRAY_FILTER_USE_BOTH) {
6527 			fci.param_count = 2;
6528 			key = &args[1];
6529 		} else {
6530 			fci.param_count = 1;
6531 			key = &args[0];
6532 		}
6533 	}
6534 
6535 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6536 		if (have_callback) {
6537 			if (use_type) {
6538 				/* Set up the key */
6539 				if (!string_key) {
6540 					ZVAL_LONG(key, num_key);
6541 				} else {
6542 					ZVAL_STR_COPY(key, string_key);
6543 				}
6544 			}
6545 			if (use_type != ARRAY_FILTER_USE_KEY) {
6546 				ZVAL_COPY(&args[0], operand);
6547 			}
6548 			fci.params = args;
6549 
6550 			if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
6551 				bool retval_true;
6552 
6553 				zval_ptr_dtor(&args[0]);
6554 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6555 					zval_ptr_dtor(&args[1]);
6556 				}
6557 				retval_true = zend_is_true(&retval);
6558 				zval_ptr_dtor(&retval);
6559 				if (!retval_true) {
6560 					continue;
6561 				}
6562 			} else {
6563 				zval_ptr_dtor(&args[0]);
6564 				if (use_type == ARRAY_FILTER_USE_BOTH) {
6565 					zval_ptr_dtor(&args[1]);
6566 				}
6567 				return;
6568 			}
6569 		} else if (!zend_is_true(operand)) {
6570 			continue;
6571 		}
6572 
6573 		if (string_key) {
6574 			operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6575 		} else {
6576 			operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6577 		}
6578 		zval_add_ref(operand);
6579 	} ZEND_HASH_FOREACH_END();
6580 }
6581 /* }}} */
6582 
6583 /* {{{ Internal function to find an array element for a user closure. */
6584 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)
6585 {
6586 	zend_ulong num_key;
6587 	zend_string *str_key;
6588 	zval retval;
6589 	zval args[2];
6590 	zval *operand;
6591 
6592 	if (result_value != NULL) {
6593   		ZVAL_UNDEF(result_value);
6594 	}
6595 
6596 	if (result_key != NULL) {
6597   		ZVAL_UNDEF(result_key);
6598 	}
6599 
6600 	if (zend_hash_num_elements(array) == 0) {
6601 		return SUCCESS;
6602 	}
6603 
6604 	ZEND_ASSERT(ZEND_FCI_INITIALIZED(fci));
6605 
6606 	fci.retval = &retval;
6607 	fci.param_count = 2;
6608 	fci.params = args;
6609 
6610 	ZEND_HASH_FOREACH_KEY_VAL(array, num_key, str_key, operand) {
6611 		/* Set up the key */
6612 		if (!str_key) {
6613 			ZVAL_LONG(&args[1], num_key);
6614 		} else {
6615 			ZVAL_STR_COPY(&args[1], str_key);
6616 		}
6617 
6618 		ZVAL_COPY(&args[0], operand);
6619 
6620 		zend_result result = zend_call_function(&fci, &fci_cache);
6621 		if (EXPECTED(result == SUCCESS)) {
6622 			int retval_true;
6623 
6624 			retval_true = zend_is_true(&retval);
6625 			zval_ptr_dtor(&retval);
6626 
6627 			/* This negates the condition, if negate_condition is true. Otherwise it does nothing with `retval_true`. */
6628 			retval_true ^= negate_condition;
6629 
6630 			if (retval_true) {
6631 				if (result_value != NULL) {
6632 					ZVAL_COPY_DEREF(result_value, &args[0]);
6633 				}
6634 
6635 				if (result_key != NULL) {
6636 					ZVAL_COPY(result_key, &args[1]);
6637 				}
6638 
6639 				zval_ptr_dtor(&args[0]);
6640 				zval_ptr_dtor(&args[1]);
6641 
6642 				return SUCCESS;
6643 			}
6644 		}
6645 
6646 		zval_ptr_dtor(&args[0]);
6647 		zval_ptr_dtor(&args[1]);
6648 
6649 		if (UNEXPECTED(result != SUCCESS)) {
6650 			return FAILURE;
6651 		}
6652 	} ZEND_HASH_FOREACH_END();
6653 
6654 	return SUCCESS;
6655 }
6656 /* }}} */
6657 
6658 /* {{{ Search within an array and returns the first found element value. */
6659 PHP_FUNCTION(array_find)
6660 {
6661 	zval *array = NULL;
6662 	zend_fcall_info fci;
6663 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6664 
6665 	ZEND_PARSE_PARAMETERS_START(2, 2)
6666 		Z_PARAM_ARRAY(array)
6667 		Z_PARAM_FUNC(fci, fci_cache)
6668 	ZEND_PARSE_PARAMETERS_END();
6669 
6670 	if (php_array_find(Z_ARR_P(array), fci, fci_cache, NULL, return_value, false) != SUCCESS) {
6671 		RETURN_THROWS();
6672 	}
6673 
6674 	if (Z_TYPE_P(return_value) == IS_UNDEF) {
6675    		RETURN_NULL();
6676 	}
6677 }
6678 /* }}} */
6679 
6680 /* {{{ Search within an array and returns the first found element key. */
6681 PHP_FUNCTION(array_find_key)
6682 {
6683 	zval *array = NULL;
6684 	zend_fcall_info fci;
6685 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6686 
6687 	ZEND_PARSE_PARAMETERS_START(2, 2)
6688 		Z_PARAM_ARRAY(array)
6689 		Z_PARAM_FUNC(fci, fci_cache)
6690 	ZEND_PARSE_PARAMETERS_END();
6691 
6692 	if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) {
6693 		RETURN_THROWS();
6694 	}
6695 
6696 	if (Z_TYPE_P(return_value) == IS_UNDEF) {
6697    		RETURN_NULL();
6698 	}
6699 }
6700 /* }}} */
6701 
6702 /* {{{ Checks if at least one array element satisfies a callback function. */
6703 PHP_FUNCTION(array_any)
6704 {
6705 	zval *array = NULL;
6706 	zend_fcall_info fci;
6707 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6708 
6709 	ZEND_PARSE_PARAMETERS_START(2, 2)
6710 		Z_PARAM_ARRAY(array)
6711 		Z_PARAM_FUNC(fci, fci_cache)
6712 	ZEND_PARSE_PARAMETERS_END();
6713 
6714 	if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) {
6715 		RETURN_THROWS();
6716 	}
6717 
6718 	RETURN_BOOL(Z_TYPE_P(return_value) != IS_UNDEF);
6719 }
6720 /* }}} */
6721 
6722 /* {{{ Checks if all array elements satisfy a callback function. */
6723 PHP_FUNCTION(array_all)
6724 {
6725 	zval *array = NULL;
6726 	zend_fcall_info fci;
6727 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6728 
6729 	ZEND_PARSE_PARAMETERS_START(2, 2)
6730 		Z_PARAM_ARRAY(array)
6731 		Z_PARAM_FUNC(fci, fci_cache)
6732 	ZEND_PARSE_PARAMETERS_END();
6733 
6734 	if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, true) != SUCCESS) {
6735 		RETURN_THROWS();
6736 	}
6737 
6738 	RETURN_BOOL(Z_TYPE_P(return_value) == IS_UNDEF);
6739 }
6740 /* }}} */
6741 
6742 /* {{{ Applies the callback to the elements in given arrays. */
6743 PHP_FUNCTION(array_map)
6744 {
6745 	zval *arrays = NULL;
6746 	int n_arrays = 0;
6747 	zval result;
6748 	zend_fcall_info fci = empty_fcall_info;
6749 	zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6750 	int i;
6751 	uint32_t k, maxlen = 0;
6752 
6753 	ZEND_PARSE_PARAMETERS_START(2, -1)
6754 		Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6755 		Z_PARAM_VARIADIC('+', arrays, n_arrays)
6756 	ZEND_PARSE_PARAMETERS_END();
6757 
6758 	if (n_arrays == 1) {
6759 		zend_ulong num_key;
6760 		zend_string *str_key;
6761 		zval *zv, arg;
6762 		int ret;
6763 
6764 		if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6765 			zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6766 			RETURN_THROWS();
6767 		}
6768 		maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
6769 
6770 		/* Short-circuit: if no callback and only one array, just return it. */
6771 		if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6772 			ZVAL_COPY(return_value, &arrays[0]);
6773 			return;
6774 		}
6775 
6776 		array_init_size(return_value, maxlen);
6777 		zend_hash_real_init(Z_ARRVAL_P(return_value), HT_IS_PACKED(Z_ARRVAL(arrays[0])));
6778 
6779 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
6780 			fci.retval = &result;
6781 			fci.param_count = 1;
6782 			fci.params = &arg;
6783 
6784 			ZVAL_COPY(&arg, zv);
6785 			ret = zend_call_function(&fci, &fci_cache);
6786 			i_zval_ptr_dtor(&arg);
6787 			if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6788 				zend_array_destroy(Z_ARR_P(return_value));
6789 				RETURN_NULL();
6790 			}
6791 			if (str_key) {
6792 				_zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6793 			} else {
6794 				zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6795 			}
6796 		} ZEND_HASH_FOREACH_END();
6797 	} else {
6798 		uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6799 
6800 		for (i = 0; i < n_arrays; i++) {
6801 			if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6802 				zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6803 				efree(array_pos);
6804 				RETURN_THROWS();
6805 			}
6806 			if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6807 				maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6808 			}
6809 		}
6810 
6811 		array_init_size(return_value, maxlen);
6812 
6813 		if (!ZEND_FCI_INITIALIZED(fci)) {
6814 			zval zv;
6815 
6816 			/* We iterate through all the arrays at once. */
6817 			for (k = 0; k < maxlen; k++) {
6818 
6819 				/* If no callback, the result will be an array, consisting of current
6820 				 * entries from all arrays. */
6821 				array_init_size(&result, n_arrays);
6822 
6823 				for (i = 0; i < n_arrays; i++) {
6824 					/* If this array still has elements, add the current one to the
6825 					 * parameter list, otherwise use null value. */
6826 					uint32_t pos = array_pos[i];
6827 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6828 						while (1) {
6829 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6830 								ZVAL_NULL(&zv);
6831 								break;
6832 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6833 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6834 								array_pos[i] = pos + 1;
6835 								break;
6836 							}
6837 							pos++;
6838 						}
6839 					} else {
6840 						while (1) {
6841 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6842 								ZVAL_NULL(&zv);
6843 								break;
6844 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6845 								ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6846 								array_pos[i] = pos + 1;
6847 								break;
6848 							}
6849 							pos++;
6850 						}
6851 					}
6852 					zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6853 				}
6854 
6855 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6856 			}
6857 		} else {
6858 			zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6859 
6860 			/* We iterate through all the arrays at once. */
6861 			for (k = 0; k < maxlen; k++) {
6862 				for (i = 0; i < n_arrays; i++) {
6863 					/* If this array still has elements, add the current one to the
6864 					 * parameter list, otherwise use null value. */
6865 					uint32_t pos = array_pos[i];
6866 					if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6867 						while (1) {
6868 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6869 								ZVAL_NULL(&params[i]);
6870 								break;
6871 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6872 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6873 								array_pos[i] = pos + 1;
6874 								break;
6875 							}
6876 							pos++;
6877 						}
6878 					} else {
6879 						while (1) {
6880 							if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6881 								ZVAL_NULL(&params[i]);
6882 								break;
6883 							} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6884 								ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6885 								array_pos[i] = pos + 1;
6886 								break;
6887 							}
6888 							pos++;
6889 						}
6890 					}
6891 				}
6892 
6893 				fci.retval = &result;
6894 				fci.param_count = n_arrays;
6895 				fci.params = params;
6896 
6897 				if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6898 					efree(array_pos);
6899 					zend_array_destroy(Z_ARR_P(return_value));
6900 					for (i = 0; i < n_arrays; i++) {
6901 						zval_ptr_dtor(&params[i]);
6902 					}
6903 					efree(params);
6904 					RETURN_NULL();
6905 				} else {
6906 					for (i = 0; i < n_arrays; i++) {
6907 						zval_ptr_dtor(&params[i]);
6908 					}
6909 				}
6910 
6911 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6912 			}
6913 
6914 			efree(params);
6915 		}
6916 		efree(array_pos);
6917 	}
6918 }
6919 /* }}} */
6920 
6921 /* {{{ Checks if the given key or index exists in the array */
6922 PHP_FUNCTION(array_key_exists)
6923 {
6924 	zval *key;
6925 	HashTable *ht;
6926 
6927 	ZEND_PARSE_PARAMETERS_START(2, 2)
6928 		Z_PARAM_ZVAL(key)
6929 		Z_PARAM_ARRAY_HT(ht)
6930 	ZEND_PARSE_PARAMETERS_END();
6931 
6932 	switch (Z_TYPE_P(key)) {
6933 		case IS_STRING:
6934 			RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6935 			break;
6936 		case IS_LONG:
6937 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6938 			break;
6939 		case IS_NULL:
6940 			RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6941 			break;
6942 		case IS_DOUBLE:
6943 			RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6944 			break;
6945 		case IS_FALSE:
6946 			RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6947 			break;
6948 		case IS_TRUE:
6949 			RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6950 			break;
6951 		case IS_RESOURCE:
6952 			zend_use_resource_as_offset(key);
6953 			RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6954 			break;
6955 		default:
6956 			zend_argument_type_error(1, "must be a valid array offset type");
6957 			break;
6958 	}
6959 }
6960 /* }}} */
6961 
6962 /* {{{ Split array into chunks */
6963 PHP_FUNCTION(array_chunk)
6964 {
6965 	int num_in;
6966 	zend_long size, current = 0;
6967 	zend_string *str_key;
6968 	zend_ulong num_key;
6969 	bool preserve_keys = 0;
6970 	zval *input = NULL;
6971 	zval chunk;
6972 	zval *entry;
6973 
6974 	ZEND_PARSE_PARAMETERS_START(2, 3)
6975 		Z_PARAM_ARRAY(input)
6976 		Z_PARAM_LONG(size)
6977 		Z_PARAM_OPTIONAL
6978 		Z_PARAM_BOOL(preserve_keys)
6979 	ZEND_PARSE_PARAMETERS_END();
6980 
6981 	/* Do bounds checking for size parameter. */
6982 	if (size < 1) {
6983 		zend_argument_value_error(2, "must be greater than 0");
6984 		RETURN_THROWS();
6985 	}
6986 
6987 	num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6988 
6989 	if (size > num_in) {
6990 		if (num_in == 0) {
6991 			RETVAL_EMPTY_ARRAY();
6992 			return;
6993 		}
6994 		size = num_in;
6995 	}
6996 
6997 	array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6998 
6999 	ZVAL_UNDEF(&chunk);
7000 
7001 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
7002 		/* If new chunk, create and initialize it. */
7003 		if (Z_TYPE(chunk) == IS_UNDEF) {
7004 			array_init_size(&chunk, (uint32_t)size);
7005 		}
7006 
7007 		/* Add entry to the chunk, preserving keys if necessary. */
7008 		if (preserve_keys) {
7009 			if (str_key) {
7010 				entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
7011 			} else {
7012 				entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
7013 			}
7014 		} else {
7015 			entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
7016 		}
7017 		zval_add_ref(entry);
7018 
7019 		/* If reached the chunk size, add it to the result array, and reset the
7020 		 * pointer. */
7021 		if (!(++current % size)) {
7022 			add_next_index_zval(return_value, &chunk);
7023 			ZVAL_UNDEF(&chunk);
7024 		}
7025 	} ZEND_HASH_FOREACH_END();
7026 
7027 	/* Add the final chunk if there is one. */
7028 	if (Z_TYPE(chunk) != IS_UNDEF) {
7029 		add_next_index_zval(return_value, &chunk);
7030 	}
7031 }
7032 /* }}} */
7033 
7034 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
7035 PHP_FUNCTION(array_combine)
7036 {
7037 	HashTable *values, *keys;
7038 	uint32_t pos_values = 0;
7039 	zval *entry_keys, *entry_values;
7040 	int num_keys, num_values;
7041 
7042 	ZEND_PARSE_PARAMETERS_START(2, 2)
7043 		Z_PARAM_ARRAY_HT(keys)
7044 		Z_PARAM_ARRAY_HT(values)
7045 	ZEND_PARSE_PARAMETERS_END();
7046 
7047 	num_keys = zend_hash_num_elements(keys);
7048 	num_values = zend_hash_num_elements(values);
7049 
7050 	if (num_keys != num_values) {
7051 		zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
7052 		RETURN_THROWS();
7053 	}
7054 
7055 	if (!num_keys) {
7056 		RETURN_EMPTY_ARRAY();
7057 	}
7058 
7059 	array_init_size(return_value, num_keys);
7060 	ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
7061 		while (1) {
7062 			if (pos_values >= values->nNumUsed) {
7063 				break;
7064 			}
7065 			entry_values = ZEND_HASH_ELEMENT(values, pos_values);
7066 			if (Z_TYPE_P(entry_values) != IS_UNDEF) {
7067 				if (Z_TYPE_P(entry_keys) == IS_LONG) {
7068 					entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
7069 						Z_LVAL_P(entry_keys), entry_values);
7070 				} else {
7071 					zend_string *tmp_key;
7072 					zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
7073 					entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
7074 						key, entry_values);
7075 					zend_tmp_string_release(tmp_key);
7076 				}
7077 				zval_add_ref(entry_values);
7078 				pos_values++;
7079 				break;
7080 			}
7081 			pos_values++;
7082 		}
7083 	} ZEND_HASH_FOREACH_END();
7084 }
7085 /* }}} */
7086