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