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