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