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