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