xref: /php-src/ext/standard/array.c (revision 50a9f511)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Andi Gutmans <andi@php.net>                                 |
14    |          Zeev Suraski <zeev@php.net>                                 |
15    |          Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Andrei Zmievski <andrei@php.net>                            |
17    |          Stig Venaas <venaas@php.net>                                |
18    |          Jason Greene <jason@php.net>                                |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef PHP_WIN32
31 #include "win32/unistd.h"
32 #endif
33 #include "zend_globals.h"
34 #include "zend_interfaces.h"
35 #include "php_globals.h"
36 #include "php_array.h"
37 #include "basic_functions.h"
38 #include "php_string.h"
39 #include "php_rand.h"
40 #include "php_math.h"
41 #include "zend_smart_str.h"
42 #include "zend_bitset.h"
43 #include "zend_exceptions.h"
44 #include "ext/spl/spl_array.h"
45 
46 /* {{{ defines */
47 #define EXTR_OVERWRITE			0
48 #define EXTR_SKIP				1
49 #define EXTR_PREFIX_SAME		2
50 #define	EXTR_PREFIX_ALL			3
51 #define	EXTR_PREFIX_INVALID		4
52 #define	EXTR_PREFIX_IF_EXISTS	5
53 #define	EXTR_IF_EXISTS			6
54 
55 #define EXTR_REFS				0x100
56 
57 #define CASE_LOWER				0
58 #define CASE_UPPER				1
59 
60 #define DIFF_NORMAL			1
61 #define DIFF_KEY			2
62 #define DIFF_ASSOC			6
63 #define DIFF_COMP_DATA_NONE    -1
64 #define DIFF_COMP_DATA_INTERNAL 0
65 #define DIFF_COMP_DATA_USER     1
66 #define DIFF_COMP_KEY_INTERNAL  0
67 #define DIFF_COMP_KEY_USER      1
68 
69 #define INTERSECT_NORMAL		1
70 #define INTERSECT_KEY			2
71 #define INTERSECT_ASSOC			6
72 #define INTERSECT_COMP_DATA_NONE    -1
73 #define INTERSECT_COMP_DATA_INTERNAL 0
74 #define INTERSECT_COMP_DATA_USER     1
75 #define INTERSECT_COMP_KEY_INTERNAL  0
76 #define INTERSECT_COMP_KEY_USER      1
77 /* }}} */
78 
ZEND_DECLARE_MODULE_GLOBALS(array)79 ZEND_DECLARE_MODULE_GLOBALS(array)
80 
81 /* {{{ php_array_init_globals
82 */
83 static void php_array_init_globals(zend_array_globals *array_globals)
84 {
85 	memset(array_globals, 0, sizeof(zend_array_globals));
86 }
87 /* }}} */
88 
PHP_MINIT_FUNCTION(array)89 PHP_MINIT_FUNCTION(array) /* {{{ */
90 {
91 	ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
92 
93 	REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
94 	REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
95 	REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
96 	REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
97 	REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
98 	REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
99 	REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
100 	REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
101 
102 	REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
103 	REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
104 
105 	REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
106 	REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
107 	REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
108 	REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
109 	REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
110 	REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
111 
112 	REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
113 	REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
114 
115 	REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
116 	REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
117 
118 	REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
119 	REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
120 
121 	return SUCCESS;
122 }
123 /* }}} */
124 
PHP_MSHUTDOWN_FUNCTION(array)125 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
126 {
127 #ifdef ZTS
128 	ts_free_id(array_globals_id);
129 #endif
130 
131 	return SUCCESS;
132 }
133 /* }}} */
134 
php_array_key_compare(Bucket * f,Bucket * s)135 static int php_array_key_compare(Bucket *f, Bucket *s) /* {{{ */
136 {
137 	zend_uchar t;
138 	zend_long l1, l2;
139 	double d;
140 
141 	if (f->key == NULL) {
142 		if (s->key == NULL) {
143 			return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
144 		} else {
145 			l1 = (zend_long)f->h;
146 			t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
147 			if (t == IS_LONG) {
148 				/* pass */
149 			} else if (t == IS_DOUBLE) {
150 				return ZEND_NORMALIZE_BOOL((double)l1 - d);
151 			} else {
152 				l2 = 0;
153 			}
154 		}
155 	} else {
156 		if (s->key) {
157 			return zendi_smart_strcmp(f->key, s->key);
158 		} else {
159 			l2 = (zend_long)s->h;
160 			t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
161 			if (t == IS_LONG) {
162 				/* pass */
163 			} else if (t == IS_DOUBLE) {
164 				return ZEND_NORMALIZE_BOOL(d - (double)l2);
165 			} else {
166 				l1 = 0;
167 			}
168 		}
169 	}
170 	return ZEND_NORMALIZE_BOOL(l1 - l2);
171 }
172 /* }}} */
173 
php_array_reverse_key_compare(Bucket * a,Bucket * b)174 static int php_array_reverse_key_compare(Bucket *a, Bucket *b) /* {{{ */
175 {
176 	return php_array_key_compare(b, a);
177 }
178 /* }}} */
179 
php_array_key_compare_numeric(Bucket * f,Bucket * s)180 static int php_array_key_compare_numeric(Bucket *f, Bucket *s) /* {{{ */
181 {
182 	if (f->key == NULL && s->key == NULL) {
183 		return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
184 	} else {
185 		double d1, d2;
186 		if (f->key) {
187 			d1 = zend_strtod(f->key->val, NULL);
188 		} else {
189 			d1 = (double)(zend_long)f->h;
190 		}
191 		if (s->key) {
192 			d2 = zend_strtod(s->key->val, NULL);
193 		} else {
194 			d2 = (double)(zend_long)s->h;
195 		}
196 		return ZEND_NORMALIZE_BOOL(d1 - d2);
197 	}
198 }
199 /* }}} */
200 
php_array_reverse_key_compare_numeric(Bucket * a,Bucket * b)201 static int php_array_reverse_key_compare_numeric(Bucket *a, Bucket *b) /* {{{ */
202 {
203 	return php_array_key_compare_numeric(b, a);
204 }
205 /* }}} */
206 
php_array_key_compare_string_case(Bucket * f,Bucket * s)207 static int php_array_key_compare_string_case(Bucket *f, Bucket *s) /* {{{ */
208 {
209 	const char *s1, *s2;
210 	size_t l1, l2;
211 	char buf1[MAX_LENGTH_OF_LONG + 1];
212 	char buf2[MAX_LENGTH_OF_LONG + 1];
213 
214 	if (f->key) {
215 		s1 = f->key->val;
216 		l1 = f->key->len;
217 	} else {
218 		s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
219 		l1 = buf1 + sizeof(buf1) - 1 - s1;
220 	}
221 	if (s->key) {
222 		s2 = s->key->val;
223 		l2 = s->key->len;
224 	} else {
225 		s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
226 		l2 = buf2 + sizeof(buf2) - 1 - s1;
227 	}
228 	return zend_binary_strcasecmp_l(s1, l1, s2, l2);
229 }
230 /* }}} */
231 
php_array_reverse_key_compare_string_case(Bucket * a,Bucket * b)232 static int php_array_reverse_key_compare_string_case(Bucket *a, Bucket *b) /* {{{ */
233 {
234 	return php_array_key_compare_string_case(b, a);
235 }
236 /* }}} */
237 
php_array_key_compare_string(Bucket * f,Bucket * s)238 static int php_array_key_compare_string(Bucket *f, Bucket *s) /* {{{ */
239 {
240 	const char *s1, *s2;
241 	size_t l1, l2;
242 	char buf1[MAX_LENGTH_OF_LONG + 1];
243 	char buf2[MAX_LENGTH_OF_LONG + 1];
244 
245 	if (f->key) {
246 		s1 = f->key->val;
247 		l1 = f->key->len;
248 	} else {
249 		s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
250 		l1 = buf1 + sizeof(buf1) - 1 - s1;
251 	}
252 	if (s->key) {
253 		s2 = s->key->val;
254 		l2 = s->key->len;
255 	} else {
256 		s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
257 		l2 = buf2 + sizeof(buf2) - 1 - s2;
258 	}
259 	return zend_binary_strcmp(s1, l1, s2, l2);
260 }
261 /* }}} */
262 
php_array_reverse_key_compare_string(Bucket * a,Bucket * b)263 static int php_array_reverse_key_compare_string(Bucket *a, Bucket *b) /* {{{ */
264 {
265 	return php_array_key_compare_string(b, a);
266 }
267 /* }}} */
268 
php_array_key_compare_string_natural_general(Bucket * f,Bucket * s,int fold_case)269 static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case) /* {{{ */
270 {
271 	const char *s1, *s2;
272 	size_t l1, l2;
273 	char buf1[MAX_LENGTH_OF_LONG + 1];
274 	char buf2[MAX_LENGTH_OF_LONG + 1];
275 
276 	if (f->key) {
277 		s1 = f->key->val;
278 		l1 = f->key->len;
279 	} else {
280 		s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
281 		l1 = buf1 + sizeof(buf1) - 1 - s1;
282 	}
283 	if (s->key) {
284 		s2 = s->key->val;
285 		l2 = s->key->len;
286 	} else {
287 		s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
288 		l2 = buf2 + sizeof(buf2) - 1 - s1;
289 	}
290 	return strnatcmp_ex(s1, l1, s2, l2, fold_case);
291 }
292 /* }}} */
293 
php_array_key_compare_string_natural_case(Bucket * a,Bucket * b)294 static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
295 {
296 	return php_array_key_compare_string_natural_general(a, b, 1);
297 }
298 /* }}} */
299 
php_array_reverse_key_compare_string_natural_case(Bucket * a,Bucket * b)300 static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
301 {
302 	return php_array_key_compare_string_natural_general(b, a, 1);
303 }
304 /* }}} */
305 
php_array_key_compare_string_natural(Bucket * a,Bucket * b)306 static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
307 {
308 	return php_array_key_compare_string_natural_general(a, b, 0);
309 }
310 /* }}} */
311 
php_array_reverse_key_compare_string_natural(Bucket * a,Bucket * b)312 static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
313 {
314 	return php_array_key_compare_string_natural_general(b, a, 0);
315 }
316 /* }}} */
317 
php_array_key_compare_string_locale(Bucket * f,Bucket * s)318 static int php_array_key_compare_string_locale(Bucket *f, Bucket *s) /* {{{ */
319 {
320 	const char *s1, *s2;
321 	char buf1[MAX_LENGTH_OF_LONG + 1];
322 	char buf2[MAX_LENGTH_OF_LONG + 1];
323 
324 	if (f->key) {
325 		s1 = f->key->val;
326 	} else {
327 		s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
328 	}
329 	if (s->key) {
330 		s2 = s->key->val;
331 	} else {
332 		s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
333 	}
334 	return strcoll(s1, s2);
335 }
336 /* }}} */
337 
php_array_reverse_key_compare_string_locale(Bucket * a,Bucket * b)338 static int php_array_reverse_key_compare_string_locale(Bucket *a, Bucket *b) /* {{{ */
339 {
340 	return php_array_key_compare_string_locale(b, a);
341 }
342 /* }}} */
343 
php_array_data_compare(Bucket * f,Bucket * s)344 static int php_array_data_compare(Bucket *f, Bucket *s) /* {{{ */
345 {
346 	zval *first = &f->val;
347 	zval *second = &s->val;
348 
349 	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
350 		first = Z_INDIRECT_P(first);
351 	}
352 	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
353 		second = Z_INDIRECT_P(second);
354 	}
355 	return zend_compare(first, second);
356 }
357 /* }}} */
358 
php_array_reverse_data_compare(Bucket * a,Bucket * b)359 static int php_array_reverse_data_compare(Bucket *a, Bucket *b) /* {{{ */
360 {
361 	return php_array_data_compare(a, b) * -1;
362 }
363 /* }}} */
364 
php_array_data_compare_numeric(Bucket * f,Bucket * s)365 static int php_array_data_compare_numeric(Bucket *f, Bucket *s) /* {{{ */
366 {
367 	zval *first = &f->val;
368 	zval *second = &s->val;
369 
370 	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
371 		first = Z_INDIRECT_P(first);
372 	}
373 	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
374 		second = Z_INDIRECT_P(second);
375 	}
376 
377 	return numeric_compare_function(first, second);
378 }
379 /* }}} */
380 
php_array_reverse_data_compare_numeric(Bucket * a,Bucket * b)381 static int php_array_reverse_data_compare_numeric(Bucket *a, Bucket *b) /* {{{ */
382 {
383 	return php_array_data_compare_numeric(b, a);
384 }
385 /* }}} */
386 
php_array_data_compare_string_case(Bucket * f,Bucket * s)387 static int php_array_data_compare_string_case(Bucket *f, Bucket *s) /* {{{ */
388 {
389 	zval *first = &f->val;
390 	zval *second = &s->val;
391 
392 	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
393 		first = Z_INDIRECT_P(first);
394 	}
395 	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
396 		second = Z_INDIRECT_P(second);
397 	}
398 
399 	return string_case_compare_function(first, second);
400 }
401 /* }}} */
402 
php_array_reverse_data_compare_string_case(Bucket * a,Bucket * b)403 static int php_array_reverse_data_compare_string_case(Bucket *a, Bucket *b) /* {{{ */
404 {
405 	return php_array_data_compare_string_case(b, a);
406 }
407 /* }}} */
408 
php_array_data_compare_string(Bucket * f,Bucket * s)409 static int php_array_data_compare_string(Bucket *f, Bucket *s) /* {{{ */
410 {
411 	zval *first = &f->val;
412 	zval *second = &s->val;
413 
414 	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
415 		first = Z_INDIRECT_P(first);
416 	}
417 	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
418 		second = Z_INDIRECT_P(second);
419 	}
420 
421 	return string_compare_function(first, second);
422 }
423 /* }}} */
424 
php_array_reverse_data_compare_string(Bucket * a,Bucket * b)425 static int php_array_reverse_data_compare_string(Bucket *a, Bucket *b) /* {{{ */
426 {
427 	return php_array_data_compare_string(b, a);
428 }
429 /* }}} */
430 
php_array_natural_general_compare(Bucket * f,Bucket * s,int fold_case)431 static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */
432 {
433 	zend_string *tmp_str1, *tmp_str2;
434 	zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
435 	zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
436 
437 	int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
438 
439 	zend_tmp_string_release(tmp_str1);
440 	zend_tmp_string_release(tmp_str2);
441 	return result;
442 }
443 /* }}} */
444 
php_array_natural_compare(Bucket * a,Bucket * b)445 static int php_array_natural_compare(Bucket *a, Bucket *b) /* {{{ */
446 {
447 	return php_array_natural_general_compare(a, b, 0);
448 }
449 /* }}} */
450 
php_array_reverse_natural_compare(Bucket * a,Bucket * b)451 static int php_array_reverse_natural_compare(Bucket *a, Bucket *b) /* {{{ */
452 {
453 	return php_array_natural_general_compare(b, a, 0);
454 }
455 /* }}} */
456 
php_array_natural_case_compare(Bucket * a,Bucket * b)457 static int php_array_natural_case_compare(Bucket *a, Bucket *b) /* {{{ */
458 {
459 	return php_array_natural_general_compare(a, b, 1);
460 }
461 /* }}} */
462 
php_array_reverse_natural_case_compare(Bucket * a,Bucket * b)463 static int php_array_reverse_natural_case_compare(Bucket *a, Bucket *b) /* {{{ */
464 {
465 	return php_array_natural_general_compare(b, a, 1);
466 }
467 /* }}} */
468 
php_array_data_compare_string_locale(Bucket * f,Bucket * s)469 static int php_array_data_compare_string_locale(Bucket *f, Bucket *s) /* {{{ */
470 {
471 	zval *first = &f->val;
472 	zval *second = &s->val;
473 
474 	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
475 		first = Z_INDIRECT_P(first);
476 	}
477 	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
478 		second = Z_INDIRECT_P(second);
479 	}
480 
481 	return string_locale_compare_function(first, second);
482 }
483 /* }}} */
484 
php_array_reverse_data_compare_string_locale(Bucket * a,Bucket * b)485 static int php_array_reverse_data_compare_string_locale(Bucket *a, Bucket *b) /* {{{ */
486 {
487 	return php_array_data_compare_string_locale(b, a);
488 }
489 /* }}} */
490 
php_get_key_compare_func(zend_long sort_type,int reverse)491 static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */
492 {
493 	switch (sort_type & ~PHP_SORT_FLAG_CASE) {
494 		case PHP_SORT_NUMERIC:
495 			if (reverse) {
496 				return php_array_reverse_key_compare_numeric;
497 			} else {
498 				return php_array_key_compare_numeric;
499 			}
500 			break;
501 
502 		case PHP_SORT_STRING:
503 			if (sort_type & PHP_SORT_FLAG_CASE) {
504 				if (reverse) {
505 					return php_array_reverse_key_compare_string_case;
506 				} else {
507 					return php_array_key_compare_string_case;
508 				}
509 			} else {
510 				if (reverse) {
511 					return php_array_reverse_key_compare_string;
512 				} else {
513 					return php_array_key_compare_string;
514 				}
515 			}
516 			break;
517 
518 		case PHP_SORT_NATURAL:
519 			if (sort_type & PHP_SORT_FLAG_CASE) {
520 				if (reverse) {
521 					return php_array_reverse_key_compare_string_natural_case;
522 				} else {
523 					return php_array_key_compare_string_natural_case;
524 				}
525 			} else {
526 				if (reverse) {
527 					return php_array_reverse_key_compare_string_natural;
528 				} else {
529 					return php_array_key_compare_string_natural;
530 				}
531 			}
532 			break;
533 
534 		case PHP_SORT_LOCALE_STRING:
535 			if (reverse) {
536 				return php_array_reverse_key_compare_string_locale;
537 			} else {
538 				return php_array_key_compare_string_locale;
539 			}
540 			break;
541 
542 		case PHP_SORT_REGULAR:
543 		default:
544 			if (reverse) {
545 				return php_array_reverse_key_compare;
546 			} else {
547 				return php_array_key_compare;
548 			}
549 			break;
550 	}
551 	return NULL;
552 }
553 /* }}} */
554 
php_get_data_compare_func(zend_long sort_type,int reverse)555 static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */
556 {
557 	switch (sort_type & ~PHP_SORT_FLAG_CASE) {
558 		case PHP_SORT_NUMERIC:
559 			if (reverse) {
560 				return php_array_reverse_data_compare_numeric;
561 			} else {
562 				return php_array_data_compare_numeric;
563 			}
564 			break;
565 
566 		case PHP_SORT_STRING:
567 			if (sort_type & PHP_SORT_FLAG_CASE) {
568 				if (reverse) {
569 					return php_array_reverse_data_compare_string_case;
570 				} else {
571 					return php_array_data_compare_string_case;
572 				}
573 			} else {
574 				if (reverse) {
575 					return php_array_reverse_data_compare_string;
576 				} else {
577 					return php_array_data_compare_string;
578 				}
579 			}
580 			break;
581 
582 		case PHP_SORT_NATURAL:
583 			if (sort_type & PHP_SORT_FLAG_CASE) {
584 				if (reverse) {
585 					return php_array_reverse_natural_case_compare;
586 				} else {
587 					return php_array_natural_case_compare;
588 				}
589 			} else {
590 				if (reverse) {
591 					return php_array_reverse_natural_compare;
592 				} else {
593 					return php_array_natural_compare;
594 				}
595 			}
596 			break;
597 
598 		case PHP_SORT_LOCALE_STRING:
599 			if (reverse) {
600 				return php_array_reverse_data_compare_string_locale;
601 			} else {
602 				return php_array_data_compare_string_locale;
603 			}
604 			break;
605 
606 		case PHP_SORT_REGULAR:
607 		default:
608 			if (reverse) {
609 				return php_array_reverse_data_compare;
610 			} else {
611 				return php_array_data_compare;
612 			}
613 			break;
614 	}
615 	return NULL;
616 }
617 /* }}} */
618 
619 /* {{{ proto bool krsort(array &array_arg [, int sort_flags])
620    Sort an array by key value in reverse order */
PHP_FUNCTION(krsort)621 PHP_FUNCTION(krsort)
622 {
623 	zval *array;
624 	zend_long sort_type = PHP_SORT_REGULAR;
625 	bucket_compare_func_t cmp;
626 
627 	ZEND_PARSE_PARAMETERS_START(1, 2)
628 		Z_PARAM_ARRAY_EX(array, 0, 1)
629 		Z_PARAM_OPTIONAL
630 		Z_PARAM_LONG(sort_type)
631 	ZEND_PARSE_PARAMETERS_END();
632 
633 	cmp = php_get_key_compare_func(sort_type, 1);
634 
635 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
636 
637 	RETURN_TRUE;
638 }
639 /* }}} */
640 
641 /* {{{ proto bool ksort(array &array_arg [, int sort_flags])
642    Sort an array by key */
PHP_FUNCTION(ksort)643 PHP_FUNCTION(ksort)
644 {
645 	zval *array;
646 	zend_long sort_type = PHP_SORT_REGULAR;
647 	bucket_compare_func_t cmp;
648 
649 	ZEND_PARSE_PARAMETERS_START(1, 2)
650 		Z_PARAM_ARRAY_EX(array, 0, 1)
651 		Z_PARAM_OPTIONAL
652 		Z_PARAM_LONG(sort_type)
653 	ZEND_PARSE_PARAMETERS_END();
654 
655 	cmp = php_get_key_compare_func(sort_type, 0);
656 
657 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
658 
659 	RETURN_TRUE;
660 }
661 /* }}} */
662 
php_count_recursive(HashTable * ht)663 PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
664 {
665 	zend_long cnt = 0;
666 	zval *element;
667 
668 	if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
669 		if (GC_IS_RECURSIVE(ht)) {
670 			php_error_docref(NULL, E_WARNING, "Recursion detected");
671 			return 0;
672 		}
673 		GC_PROTECT_RECURSION(ht);
674 	}
675 
676 	cnt = zend_array_count(ht);
677 	ZEND_HASH_FOREACH_VAL(ht, element) {
678 		ZVAL_DEREF(element);
679 		if (Z_TYPE_P(element) == IS_ARRAY) {
680 			cnt += php_count_recursive(Z_ARRVAL_P(element));
681 		}
682 	} ZEND_HASH_FOREACH_END();
683 
684 	if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
685 		GC_UNPROTECT_RECURSION(ht);
686 	}
687 
688 	return cnt;
689 }
690 /* }}} */
691 
692 /* {{{ proto int count(mixed var [, int mode])
693    Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)694 PHP_FUNCTION(count)
695 {
696 	zval *array;
697 	zend_long mode = COUNT_NORMAL;
698 	zend_long cnt;
699 
700 	ZEND_PARSE_PARAMETERS_START(1, 2)
701 		Z_PARAM_ZVAL(array)
702 		Z_PARAM_OPTIONAL
703 		Z_PARAM_LONG(mode)
704 	ZEND_PARSE_PARAMETERS_END();
705 
706 	if (mode != COUNT_NORMAL && mode != COUNT_RECURSIVE) {
707 		zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
708 		RETURN_THROWS();
709 	}
710 
711 	switch (Z_TYPE_P(array)) {
712 		case IS_NULL:
713 			/* Intentionally not converted to an exception */
714 			php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
715 			RETURN_LONG(0);
716 			break;
717 		case IS_ARRAY:
718 			if (mode != COUNT_RECURSIVE) {
719 				cnt = zend_array_count(Z_ARRVAL_P(array));
720 			} else {
721 				cnt = php_count_recursive(Z_ARRVAL_P(array));
722 			}
723 			RETURN_LONG(cnt);
724 			break;
725 		case IS_OBJECT: {
726 			zval retval;
727 			/* first, we check if the handler is defined */
728 			if (Z_OBJ_HT_P(array)->count_elements) {
729 				RETVAL_LONG(1);
730 				if (SUCCESS == Z_OBJ_HT(*array)->count_elements(Z_OBJ_P(array), &Z_LVAL_P(return_value))) {
731 					return;
732 				}
733 				if (EG(exception)) {
734 					RETURN_THROWS();
735 				}
736 			}
737 			/* if not and the object implements Countable we call its count() method */
738 			if (instanceof_function(Z_OBJCE_P(array), zend_ce_countable)) {
739 				zend_call_method_with_0_params(Z_OBJ_P(array), NULL, NULL, "count", &retval);
740 				if (Z_TYPE(retval) != IS_UNDEF) {
741 					RETVAL_LONG(zval_get_long(&retval));
742 					zval_ptr_dtor(&retval);
743 				}
744 				return;
745 			}
746 
747 			/* If There's no handler and it doesn't implement Countable then add a warning */
748 			/* Intentionally not converted to an exception */
749 			php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
750 			RETURN_LONG(1);
751 			break;
752 		}
753 		default:
754 			/* Intentionally not converted to an exception */
755 			php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
756 			RETURN_LONG(1);
757 			break;
758 	}
759 }
760 /* }}} */
761 
php_natsort(INTERNAL_FUNCTION_PARAMETERS,int fold_case)762 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
763 {
764 	zval *array;
765 
766 	ZEND_PARSE_PARAMETERS_START(1, 1)
767 		Z_PARAM_ARRAY_EX(array, 0, 1)
768 	ZEND_PARSE_PARAMETERS_END();
769 
770 	if (fold_case) {
771 		zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
772 	} else {
773 		zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
774 	}
775 
776 	RETURN_TRUE;
777 }
778 /* }}} */
779 
780 /* {{{ proto void natsort(array &array_arg)
781    Sort an array using natural sort */
PHP_FUNCTION(natsort)782 PHP_FUNCTION(natsort)
783 {
784 	php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
785 }
786 /* }}} */
787 
788 /* {{{ proto void natcasesort(array &array_arg)
789    Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)790 PHP_FUNCTION(natcasesort)
791 {
792 	php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
793 }
794 /* }}} */
795 
796 /* {{{ proto bool asort(array &array_arg [, int sort_flags])
797    Sort an array and maintain index association */
PHP_FUNCTION(asort)798 PHP_FUNCTION(asort)
799 {
800 	zval *array;
801 	zend_long sort_type = PHP_SORT_REGULAR;
802 	bucket_compare_func_t cmp;
803 
804 	ZEND_PARSE_PARAMETERS_START(1, 2)
805 		Z_PARAM_ARRAY_EX(array, 0, 1)
806 		Z_PARAM_OPTIONAL
807 		Z_PARAM_LONG(sort_type)
808 	ZEND_PARSE_PARAMETERS_END();
809 
810 	cmp = php_get_data_compare_func(sort_type, 0);
811 
812 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
813 
814 	RETURN_TRUE;
815 }
816 /* }}} */
817 
818 /* {{{ proto bool arsort(array &array_arg [, int sort_flags])
819    Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)820 PHP_FUNCTION(arsort)
821 {
822 	zval *array;
823 	zend_long sort_type = PHP_SORT_REGULAR;
824 	bucket_compare_func_t cmp;
825 
826 	ZEND_PARSE_PARAMETERS_START(1, 2)
827 		Z_PARAM_ARRAY_EX(array, 0, 1)
828 		Z_PARAM_OPTIONAL
829 		Z_PARAM_LONG(sort_type)
830 	ZEND_PARSE_PARAMETERS_END();
831 
832 	cmp = php_get_data_compare_func(sort_type, 1);
833 
834 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
835 
836 	RETURN_TRUE;
837 }
838 /* }}} */
839 
840 /* {{{ proto bool sort(array &array_arg [, int sort_flags])
841    Sort an array */
PHP_FUNCTION(sort)842 PHP_FUNCTION(sort)
843 {
844 	zval *array;
845 	zend_long sort_type = PHP_SORT_REGULAR;
846 	bucket_compare_func_t cmp;
847 
848 	ZEND_PARSE_PARAMETERS_START(1, 2)
849 		Z_PARAM_ARRAY_EX(array, 0, 1)
850 		Z_PARAM_OPTIONAL
851 		Z_PARAM_LONG(sort_type)
852 	ZEND_PARSE_PARAMETERS_END();
853 
854 	cmp = php_get_data_compare_func(sort_type, 0);
855 
856 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
857 
858 	RETURN_TRUE;
859 }
860 /* }}} */
861 
862 /* {{{ proto bool rsort(array &array_arg [, int sort_flags])
863    Sort an array in reverse order */
PHP_FUNCTION(rsort)864 PHP_FUNCTION(rsort)
865 {
866 	zval *array;
867 	zend_long sort_type = PHP_SORT_REGULAR;
868 	bucket_compare_func_t cmp;
869 
870 	ZEND_PARSE_PARAMETERS_START(1, 2)
871 		Z_PARAM_ARRAY_EX(array, 0, 1)
872 		Z_PARAM_OPTIONAL
873 		Z_PARAM_LONG(sort_type)
874 	ZEND_PARSE_PARAMETERS_END();
875 
876 	cmp = php_get_data_compare_func(sort_type, 1);
877 
878 	zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
879 
880 	RETURN_TRUE;
881 }
882 /* }}} */
883 
php_array_user_compare(Bucket * f,Bucket * s)884 static int php_array_user_compare(Bucket *f, Bucket *s) /* {{{ */
885 {
886 	zval args[2];
887 	zval retval;
888 
889 	ZVAL_COPY(&args[0], &f->val);
890 	ZVAL_COPY(&args[1], &s->val);
891 
892 	BG(user_compare_fci).param_count = 2;
893 	BG(user_compare_fci).params = args;
894 	BG(user_compare_fci).retval = &retval;
895 	BG(user_compare_fci).no_separation = 0;
896 	if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
897 		zend_long ret = zval_get_long(&retval);
898 		zval_ptr_dtor(&retval);
899 		zval_ptr_dtor(&args[1]);
900 		zval_ptr_dtor(&args[0]);
901 		return ZEND_NORMALIZE_BOOL(ret);
902 	} else {
903 		zval_ptr_dtor(&args[1]);
904 		zval_ptr_dtor(&args[0]);
905 		return 0;
906 	}
907 }
908 /* }}} */
909 
910 /* check if comparison function is valid */
911 #define PHP_ARRAY_CMP_FUNC_CHECK(func_name)	\
912 	if (!zend_is_callable(*func_name, 0, NULL)) {	\
913 		php_error_docref(NULL, E_WARNING, "Invalid comparison function");	\
914 		BG(user_compare_fci) = old_user_compare_fci; \
915 		BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
916 		RETURN_FALSE;	\
917 	}	\
918 
919 	/* Clear FCI cache otherwise : for example the same or other array with
920 	 * (partly) the same key values has been sorted with uasort() or
921 	 * other sorting function the comparison is cached, however the name
922 	 * of the function for comparison is not respected. see bug #28739 AND #33295
923 	 *
924 	 * Following defines will assist in backup / restore values. */
925 
926 #define PHP_ARRAY_CMP_FUNC_VARS \
927 	zend_fcall_info old_user_compare_fci; \
928 	zend_fcall_info_cache old_user_compare_fci_cache \
929 
930 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
931 	old_user_compare_fci = BG(user_compare_fci); \
932 	old_user_compare_fci_cache = BG(user_compare_fci_cache); \
933 	BG(user_compare_fci_cache) = empty_fcall_info_cache; \
934 
935 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
936 	zend_release_fcall_info_cache(&BG(user_compare_fci_cache)); \
937 	BG(user_compare_fci) = old_user_compare_fci; \
938 	BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
939 
php_usort(INTERNAL_FUNCTION_PARAMETERS,bucket_compare_func_t compare_func,zend_bool renumber)940 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, zend_bool renumber) /* {{{ */
941 {
942 	zval *array;
943 	zend_array *arr;
944 	PHP_ARRAY_CMP_FUNC_VARS;
945 
946 	PHP_ARRAY_CMP_FUNC_BACKUP();
947 
948 	ZEND_PARSE_PARAMETERS_START(2, 2)
949 		Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
950 		Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
951 	ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
952 
953 	arr = Z_ARR_P(array);
954 	if (zend_hash_num_elements(arr) == 0)  {
955 		PHP_ARRAY_CMP_FUNC_RESTORE();
956 		RETURN_TRUE;
957 	}
958 
959 	/* Copy array, so the in-place modifications will not be visible to the callback function */
960 	arr = zend_array_dup(arr);
961 
962 	zend_hash_sort(arr, compare_func, renumber);
963 
964 	zval_ptr_dtor(array);
965 	ZVAL_ARR(array, arr);
966 
967 	PHP_ARRAY_CMP_FUNC_RESTORE();
968 	RETURN_TRUE;
969 }
970 /* }}} */
971 
972 /* {{{ proto bool usort(array array_arg, string cmp_function)
973    Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)974 PHP_FUNCTION(usort)
975 {
976 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
977 }
978 /* }}} */
979 
980 /* {{{ proto bool uasort(array array_arg, string cmp_function)
981    Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)982 PHP_FUNCTION(uasort)
983 {
984 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
985 }
986 /* }}} */
987 
php_array_user_key_compare(Bucket * f,Bucket * s)988 static int php_array_user_key_compare(Bucket *f, Bucket *s) /* {{{ */
989 {
990 	zval args[2];
991 	zval retval;
992 	zend_long result;
993 
994 	if (f->key == NULL) {
995 		ZVAL_LONG(&args[0], f->h);
996 	} else {
997 		ZVAL_STR_COPY(&args[0], f->key);
998 	}
999 	if (s->key == NULL) {
1000 		ZVAL_LONG(&args[1], s->h);
1001 	} else {
1002 		ZVAL_STR_COPY(&args[1], s->key);
1003 	}
1004 
1005 	BG(user_compare_fci).param_count = 2;
1006 	BG(user_compare_fci).params = args;
1007 	BG(user_compare_fci).retval = &retval;
1008 	BG(user_compare_fci).no_separation = 0;
1009 	if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1010 		result = zval_get_long(&retval);
1011 		zval_ptr_dtor(&retval);
1012 	} else {
1013 		result = 0;
1014 	}
1015 
1016 	zval_ptr_dtor(&args[0]);
1017 	zval_ptr_dtor(&args[1]);
1018 
1019 	return ZEND_NORMALIZE_BOOL(result);
1020 }
1021 /* }}} */
1022 
1023 /* {{{ proto bool uksort(array array_arg, string cmp_function)
1024    Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)1025 PHP_FUNCTION(uksort)
1026 {
1027 	php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1028 }
1029 /* }}} */
1030 
1031 /* {{{ proto mixed end(array array_arg)
1032    Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1033 PHP_FUNCTION(end)
1034 {
1035 	HashTable *array;
1036 	zval *entry;
1037 
1038 	ZEND_PARSE_PARAMETERS_START(1, 1)
1039 		Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1040 	ZEND_PARSE_PARAMETERS_END();
1041 
1042 	zend_hash_internal_pointer_end(array);
1043 
1044 	if (USED_RET()) {
1045 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1046 			RETURN_FALSE;
1047 		}
1048 
1049 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1050 			entry = Z_INDIRECT_P(entry);
1051 		}
1052 
1053 		ZVAL_COPY_DEREF(return_value, entry);
1054 	}
1055 }
1056 /* }}} */
1057 
1058 /* {{{ proto mixed prev(array array_arg)
1059    Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1060 PHP_FUNCTION(prev)
1061 {
1062 	HashTable *array;
1063 	zval *entry;
1064 
1065 	ZEND_PARSE_PARAMETERS_START(1, 1)
1066 		Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1067 	ZEND_PARSE_PARAMETERS_END();
1068 
1069 	zend_hash_move_backwards(array);
1070 
1071 	if (USED_RET()) {
1072 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1073 			RETURN_FALSE;
1074 		}
1075 
1076 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1077 			entry = Z_INDIRECT_P(entry);
1078 		}
1079 
1080 		ZVAL_COPY_DEREF(return_value, entry);
1081 	}
1082 }
1083 /* }}} */
1084 
1085 /* {{{ proto mixed next(array array_arg)
1086    Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1087 PHP_FUNCTION(next)
1088 {
1089 	HashTable *array;
1090 	zval *entry;
1091 
1092 	ZEND_PARSE_PARAMETERS_START(1, 1)
1093 		Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1094 	ZEND_PARSE_PARAMETERS_END();
1095 
1096 	zend_hash_move_forward(array);
1097 
1098 	if (USED_RET()) {
1099 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1100 			RETURN_FALSE;
1101 		}
1102 
1103 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1104 			entry = Z_INDIRECT_P(entry);
1105 		}
1106 
1107 		ZVAL_COPY_DEREF(return_value, entry);
1108 	}
1109 }
1110 /* }}} */
1111 
1112 /* {{{ proto mixed reset(array array_arg)
1113    Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1114 PHP_FUNCTION(reset)
1115 {
1116 	HashTable *array;
1117 	zval *entry;
1118 
1119 	ZEND_PARSE_PARAMETERS_START(1, 1)
1120 		Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1121 	ZEND_PARSE_PARAMETERS_END();
1122 
1123 	zend_hash_internal_pointer_reset(array);
1124 
1125 	if (USED_RET()) {
1126 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1127 			RETURN_FALSE;
1128 		}
1129 
1130 		if (Z_TYPE_P(entry) == IS_INDIRECT) {
1131 			entry = Z_INDIRECT_P(entry);
1132 		}
1133 
1134 		ZVAL_COPY_DEREF(return_value, entry);
1135 	}
1136 }
1137 /* }}} */
1138 
1139 /* {{{ proto mixed current(array array_arg)
1140    Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1141 PHP_FUNCTION(current)
1142 {
1143 	HashTable *array;
1144 	zval *entry;
1145 
1146 	ZEND_PARSE_PARAMETERS_START(1, 1)
1147 		Z_PARAM_ARRAY_OR_OBJECT_HT(array)
1148 	ZEND_PARSE_PARAMETERS_END();
1149 
1150 	if ((entry = zend_hash_get_current_data(array)) == NULL) {
1151 		RETURN_FALSE;
1152 	}
1153 
1154 	if (Z_TYPE_P(entry) == IS_INDIRECT) {
1155 		entry = Z_INDIRECT_P(entry);
1156 	}
1157 
1158 	ZVAL_COPY_DEREF(return_value, entry);
1159 }
1160 /* }}} */
1161 
1162 /* {{{ proto mixed key(array array_arg)
1163    Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1164 PHP_FUNCTION(key)
1165 {
1166 	HashTable *array;
1167 
1168 	ZEND_PARSE_PARAMETERS_START(1, 1)
1169 		Z_PARAM_ARRAY_OR_OBJECT_HT(array)
1170 	ZEND_PARSE_PARAMETERS_END();
1171 
1172 	zend_hash_get_current_key_zval(array, return_value);
1173 }
1174 /* }}} */
1175 
1176 /* {{{
1177  * proto mixed min(array values)
1178  * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1179    Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1180 PHP_FUNCTION(min)
1181 {
1182 	int argc;
1183 	zval *args = NULL;
1184 
1185 	ZEND_PARSE_PARAMETERS_START(1, -1)
1186 		Z_PARAM_VARIADIC('+', args, argc)
1187 	ZEND_PARSE_PARAMETERS_END();
1188 
1189 	/* mixed min ( array $values ) */
1190 	if (argc == 1) {
1191 		zval *result;
1192 
1193 		if (Z_TYPE(args[0]) != IS_ARRAY) {
1194 			zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
1195 			RETURN_THROWS();
1196 		} else {
1197 			if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) {
1198 				ZVAL_COPY_DEREF(return_value, result);
1199 			} else {
1200 				zend_argument_value_error(1, "must contain at least one element");
1201 				RETURN_THROWS();
1202 			}
1203 		}
1204 	} else {
1205 		/* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1206 		zval *min, result;
1207 		int i;
1208 
1209 		min = &args[0];
1210 
1211 		for (i = 1; i < argc; i++) {
1212 			is_smaller_function(&result, &args[i], min);
1213 			if (Z_TYPE(result) == IS_TRUE) {
1214 				min = &args[i];
1215 			}
1216 		}
1217 
1218 		ZVAL_COPY(return_value, min);
1219 	}
1220 }
1221 /* }}} */
1222 
1223 /* {{{
1224  * proto mixed max(array values)
1225  * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1226    Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)1227 PHP_FUNCTION(max)
1228 {
1229 	zval *args = NULL;
1230 	int argc;
1231 
1232 	ZEND_PARSE_PARAMETERS_START(1, -1)
1233 		Z_PARAM_VARIADIC('+', args, argc)
1234 	ZEND_PARSE_PARAMETERS_END();
1235 
1236 	/* mixed max ( array $values ) */
1237 	if (argc == 1) {
1238 		zval *result;
1239 
1240 		if (Z_TYPE(args[0]) != IS_ARRAY) {
1241 			zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
1242 			RETURN_THROWS();
1243 		} else {
1244 			if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) {
1245 				ZVAL_COPY_DEREF(return_value, result);
1246 			} else {
1247 				zend_argument_value_error(1, "must contain at least one element");
1248 				RETURN_THROWS();
1249 			}
1250 		}
1251 	} else {
1252 		/* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1253 		zval *max, result;
1254 		int i;
1255 
1256 		max = &args[0];
1257 
1258 		for (i = 1; i < argc; i++) {
1259 			is_smaller_or_equal_function(&result, &args[i], max);
1260 			if (Z_TYPE(result) == IS_FALSE) {
1261 				max = &args[i];
1262 			}
1263 		}
1264 
1265 		ZVAL_COPY(return_value, max);
1266 	}
1267 }
1268 /* }}} */
1269 
php_array_walk(zval * array,zval * userdata,int recursive)1270 static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
1271 {
1272 	zval args[3],		/* Arguments to userland function */
1273 		 retval,		/* Return value - unused */
1274 		 *zv;
1275 	HashTable *target_hash = HASH_OF(array);
1276 	HashPosition pos;
1277 	uint32_t ht_iter;
1278 	int result = SUCCESS;
1279 
1280 	/* Set up known arguments */
1281 	ZVAL_UNDEF(&args[1]);
1282 	if (userdata) {
1283 		ZVAL_COPY(&args[2], userdata);
1284 	}
1285 
1286 	BG(array_walk_fci).retval = &retval;
1287 	BG(array_walk_fci).param_count = userdata ? 3 : 2;
1288 	BG(array_walk_fci).params = args;
1289 	BG(array_walk_fci).no_separation = 0;
1290 
1291 	zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1292 	ht_iter = zend_hash_iterator_add(target_hash, pos);
1293 
1294 	/* Iterate through hash */
1295 	do {
1296 		/* Retrieve value */
1297 		zv = zend_hash_get_current_data_ex(target_hash, &pos);
1298 		if (zv == NULL) {
1299 			break;
1300 		}
1301 
1302 		/* Skip undefined indirect elements */
1303 		if (Z_TYPE_P(zv) == IS_INDIRECT) {
1304 			zv = Z_INDIRECT_P(zv);
1305 			if (Z_TYPE_P(zv) == IS_UNDEF) {
1306 				zend_hash_move_forward_ex(target_hash, &pos);
1307 				continue;
1308 			}
1309 		}
1310 
1311 		/* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1312 		ZVAL_MAKE_REF(zv);
1313 
1314 		/* Retrieve key */
1315 		zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1316 
1317 		/* Move to next element already now -- this mirrors the approach used by foreach
1318 		 * and ensures proper behavior with regard to modifications. */
1319 		zend_hash_move_forward_ex(target_hash, &pos);
1320 
1321 		/* Back up hash position, as it may change */
1322 		EG(ht_iterators)[ht_iter].pos = pos;
1323 
1324 		if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1325 			HashTable *thash;
1326 			zend_fcall_info orig_array_walk_fci;
1327 			zend_fcall_info_cache orig_array_walk_fci_cache;
1328 			zval ref;
1329 			ZVAL_COPY_VALUE(&ref, zv);
1330 
1331 			ZVAL_DEREF(zv);
1332 			SEPARATE_ARRAY(zv);
1333 			thash = Z_ARRVAL_P(zv);
1334 			if (GC_IS_RECURSIVE(thash)) {
1335 				zend_throw_error(NULL, "Recursion detected");
1336 				result = FAILURE;
1337 				break;
1338 			}
1339 
1340 			/* backup the fcall info and cache */
1341 			orig_array_walk_fci = BG(array_walk_fci);
1342 			orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1343 
1344 			Z_ADDREF(ref);
1345 			GC_PROTECT_RECURSION(thash);
1346 			result = php_array_walk(zv, userdata, recursive);
1347 			if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1348 				/* If the hashtable changed in the meantime, we'll "leak" this apply count
1349 				 * increment -- our reference to thash is no longer valid. */
1350 				GC_UNPROTECT_RECURSION(thash);
1351 			}
1352 			zval_ptr_dtor(&ref);
1353 
1354 			/* restore the fcall info and cache */
1355 			BG(array_walk_fci) = orig_array_walk_fci;
1356 			BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1357 		} else {
1358 			ZVAL_COPY(&args[0], zv);
1359 
1360 			/* Call the userland function */
1361 			result = zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache));
1362 			if (result == SUCCESS) {
1363 				zval_ptr_dtor(&retval);
1364 			}
1365 
1366 			zval_ptr_dtor(&args[0]);
1367 		}
1368 
1369 		if (Z_TYPE(args[1]) != IS_UNDEF) {
1370 			zval_ptr_dtor(&args[1]);
1371 			ZVAL_UNDEF(&args[1]);
1372 		}
1373 
1374 		if (result == FAILURE) {
1375 			break;
1376 		}
1377 
1378 		/* Reload array and position -- both may have changed */
1379 		if (Z_TYPE_P(array) == IS_ARRAY) {
1380 			pos = zend_hash_iterator_pos_ex(ht_iter, array);
1381 			target_hash = Z_ARRVAL_P(array);
1382 		} else if (Z_TYPE_P(array) == IS_OBJECT) {
1383 			target_hash = Z_OBJPROP_P(array);
1384 			pos = zend_hash_iterator_pos(ht_iter, target_hash);
1385 		} else {
1386 			zend_type_error("Iterated value is no longer an array or object");
1387 			result = FAILURE;
1388 			break;
1389 		}
1390 	} while (!EG(exception));
1391 
1392 	if (userdata) {
1393 		zval_ptr_dtor(&args[2]);
1394 	}
1395 	zend_hash_iterator_del(ht_iter);
1396 	return result;
1397 }
1398 /* }}} */
1399 
1400 /* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
1401    Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1402 PHP_FUNCTION(array_walk)
1403 {
1404 	zval *array;
1405 	zval *userdata = NULL;
1406 	zend_fcall_info orig_array_walk_fci;
1407 	zend_fcall_info_cache orig_array_walk_fci_cache;
1408 
1409 	orig_array_walk_fci = BG(array_walk_fci);
1410 	orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1411 
1412 	ZEND_PARSE_PARAMETERS_START(2, 3)
1413 		Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1414 		Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
1415 		Z_PARAM_OPTIONAL
1416 		Z_PARAM_ZVAL(userdata)
1417 	ZEND_PARSE_PARAMETERS_END_EX(
1418 		BG(array_walk_fci) = orig_array_walk_fci;
1419 		BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1420 		return
1421 	);
1422 
1423 	php_array_walk(array, userdata, 0);
1424 	zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
1425 	BG(array_walk_fci) = orig_array_walk_fci;
1426 	BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1427 	RETURN_TRUE;
1428 }
1429 /* }}} */
1430 
1431 /* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
1432    Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1433 PHP_FUNCTION(array_walk_recursive)
1434 {
1435 	zval *array;
1436 	zval *userdata = NULL;
1437 	zend_fcall_info orig_array_walk_fci;
1438 	zend_fcall_info_cache orig_array_walk_fci_cache;
1439 
1440 	orig_array_walk_fci = BG(array_walk_fci);
1441 	orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1442 
1443 	ZEND_PARSE_PARAMETERS_START(2, 3)
1444 		Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1445 		Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
1446 		Z_PARAM_OPTIONAL
1447 		Z_PARAM_ZVAL(userdata)
1448 	ZEND_PARSE_PARAMETERS_END_EX(
1449 		BG(array_walk_fci) = orig_array_walk_fci;
1450 		BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1451 		return
1452 	);
1453 
1454 	php_array_walk(array, userdata, 1);
1455 	zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
1456 	BG(array_walk_fci) = orig_array_walk_fci;
1457 	BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1458 	RETURN_TRUE;
1459 }
1460 /* }}} */
1461 
1462 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1463  * 0 = return boolean
1464  * 1 = return key
1465  */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1466 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1467 {
1468 	zval *value,				/* value to check for */
1469 		 *array,				/* array to check in */
1470 		 *entry;				/* pointer to array entry */
1471 	zend_ulong num_idx;
1472 	zend_string *str_idx;
1473 	zend_bool strict = 0;		/* strict comparison or not */
1474 
1475 	ZEND_PARSE_PARAMETERS_START(2, 3)
1476 		Z_PARAM_ZVAL(value)
1477 		Z_PARAM_ARRAY(array)
1478 		Z_PARAM_OPTIONAL
1479 		Z_PARAM_BOOL(strict)
1480 	ZEND_PARSE_PARAMETERS_END();
1481 
1482 	if (strict) {
1483 		if (Z_TYPE_P(value) == IS_LONG) {
1484 			ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1485 				ZVAL_DEREF(entry);
1486 				if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1487 					if (behavior == 0) {
1488 						RETURN_TRUE;
1489 					} else {
1490 						if (str_idx) {
1491 							RETVAL_STR_COPY(str_idx);
1492 						} else {
1493 							RETVAL_LONG(num_idx);
1494 						}
1495 						return;
1496 					}
1497 				}
1498 			} ZEND_HASH_FOREACH_END();
1499 		} else {
1500 			ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1501 				ZVAL_DEREF(entry);
1502 				if (fast_is_identical_function(value, entry)) {
1503 					if (behavior == 0) {
1504 						RETURN_TRUE;
1505 					} else {
1506 						if (str_idx) {
1507 							RETVAL_STR_COPY(str_idx);
1508 						} else {
1509 							RETVAL_LONG(num_idx);
1510 						}
1511 						return;
1512 					}
1513 				}
1514 			} ZEND_HASH_FOREACH_END();
1515 		}
1516 	} else {
1517 		if (Z_TYPE_P(value) == IS_LONG) {
1518 			ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1519 				if (fast_equal_check_long(value, entry)) {
1520 					if (behavior == 0) {
1521 						RETURN_TRUE;
1522 					} else {
1523 						if (str_idx) {
1524 							RETVAL_STR_COPY(str_idx);
1525 						} else {
1526 							RETVAL_LONG(num_idx);
1527 						}
1528 						return;
1529 					}
1530 				}
1531 			} ZEND_HASH_FOREACH_END();
1532 		} else if (Z_TYPE_P(value) == IS_STRING) {
1533 			ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1534 				if (fast_equal_check_string(value, entry)) {
1535 					if (behavior == 0) {
1536 						RETURN_TRUE;
1537 					} else {
1538 						if (str_idx) {
1539 							RETVAL_STR_COPY(str_idx);
1540 						} else {
1541 							RETVAL_LONG(num_idx);
1542 						}
1543 						return;
1544 					}
1545 				}
1546 			} ZEND_HASH_FOREACH_END();
1547 		} else {
1548 			ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1549 				if (fast_equal_check_function(value, entry)) {
1550 					if (behavior == 0) {
1551 						RETURN_TRUE;
1552 					} else {
1553 						if (str_idx) {
1554 							RETVAL_STR_COPY(str_idx);
1555 						} else {
1556 							RETVAL_LONG(num_idx);
1557 						}
1558 						return;
1559 					}
1560 				}
1561 			} ZEND_HASH_FOREACH_END();
1562  		}
1563 	}
1564 
1565 	RETURN_FALSE;
1566 }
1567 /* }}} */
1568 
1569 /* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
1570    Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1571 PHP_FUNCTION(in_array)
1572 {
1573 	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1574 }
1575 /* }}} */
1576 
1577 /* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
1578    Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1579 PHP_FUNCTION(array_search)
1580 {
1581 	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1582 }
1583 /* }}} */
1584 
php_valid_var_name(const char * var_name,size_t var_name_len)1585 static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
1586 {
1587 #if 1
1588 	/* first 256 bits for first character, and second 256 bits for the next */
1589 	static const uint32_t charset[8] = {
1590 	     /*  31      0   63     32   95     64   127    96 */
1591 			0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1592 			0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1593 	static const uint32_t charset2[8] = {
1594 	     /*  31      0   63     32   95     64   127    96 */
1595 			0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1596 			0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1597 #endif
1598 	size_t i;
1599 	uint32_t ch;
1600 
1601 	if (UNEXPECTED(!var_name_len)) {
1602 		return 0;
1603 	}
1604 
1605 	/* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1606 	ch = (uint32_t)((unsigned char *)var_name)[0];
1607 #if 1
1608 	if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1609 #else
1610 	if (var_name[0] != '_' &&
1611 		(ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1612 		(ch < 97  /* a    */ || /* z    */ ch > 122) &&
1613 		(ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1614 	) {
1615 #endif
1616 		return 0;
1617 	}
1618 
1619 	/* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1620 	if (var_name_len > 1) {
1621 		i = 1;
1622 		do {
1623 			ch = (uint32_t)((unsigned char *)var_name)[i];
1624 #if 1
1625 			if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1626 #else
1627 			if (var_name[i] != '_' &&
1628 				(ch < 48  /* 0    */ || /* 9    */ ch > 57)  &&
1629 				(ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1630 				(ch < 97  /* a    */ || /* z    */ ch > 122) &&
1631 				(ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1632 			) {
1633 #endif
1634 				return 0;
1635 			}
1636 		} while (++i < var_name_len);
1637 	}
1638 	return 1;
1639 }
1640 /* }}} */
1641 
1642 PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, zend_bool add_underscore) /* {{{ */
1643 {
1644 	ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1645 	memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
1646 
1647 	if (add_underscore) {
1648 		Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
1649 	}
1650 
1651 	memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1652 
1653 	return SUCCESS;
1654 }
1655 /* }}} */
1656 
1657 static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1658 {
1659 	zend_long count = 0;
1660 	zend_string *var_name;
1661 	zval *entry, *orig_var;
1662 
1663 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1664 		if (!var_name) {
1665 			continue;
1666 		}
1667 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1668 		if (orig_var) {
1669 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1670 				orig_var = Z_INDIRECT_P(orig_var);
1671 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1672 					continue;
1673 				}
1674 			}
1675 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1676 				continue;
1677 			}
1678 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1679 				continue;
1680 			}
1681 			if (zend_string_equals_literal(var_name, "this")) {
1682 				zend_throw_error(NULL, "Cannot re-assign $this");
1683 				return -1;
1684 			}
1685 			if (Z_ISREF_P(entry)) {
1686 				Z_ADDREF_P(entry);
1687 			} else {
1688 				ZVAL_MAKE_REF_EX(entry, 2);
1689 			}
1690 			zval_ptr_dtor(orig_var);
1691 			ZVAL_REF(orig_var, Z_REF_P(entry));
1692 			count++;
1693 		}
1694 	} ZEND_HASH_FOREACH_END();
1695 
1696 	return count;
1697 }
1698 /* }}} */
1699 
1700 static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1701 {
1702 	zend_long count = 0;
1703 	zend_string *var_name;
1704 	zval *entry, *orig_var;
1705 
1706 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1707 		if (!var_name) {
1708 			continue;
1709 		}
1710 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1711 		if (orig_var) {
1712 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1713 				orig_var = Z_INDIRECT_P(orig_var);
1714 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1715 					continue;
1716 				}
1717 			}
1718 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1719 				continue;
1720 			}
1721 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1722 				continue;
1723 			}
1724 			if (zend_string_equals_literal(var_name, "this")) {
1725 				zend_throw_error(NULL, "Cannot re-assign $this");
1726 				return -1;
1727 			}
1728 			ZVAL_DEREF(entry);
1729 			ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1730 			if (UNEXPECTED(EG(exception))) {
1731 				return -1;
1732 			}
1733 			count++;
1734 		}
1735 	} ZEND_HASH_FOREACH_END();
1736 
1737 	return count;
1738 }
1739 /* }}} */
1740 
1741 static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1742 {
1743 	zend_long count = 0;
1744 	zend_string *var_name;
1745 	zval *entry, *orig_var;
1746 
1747 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1748 		if (!var_name) {
1749 			continue;
1750 		}
1751 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1752 			continue;
1753 		}
1754 		if (zend_string_equals_literal(var_name, "this")) {
1755 			zend_throw_error(NULL, "Cannot re-assign $this");
1756 			return -1;
1757 		}
1758 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1759 		if (orig_var) {
1760 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1761 				orig_var = Z_INDIRECT_P(orig_var);
1762 			}
1763 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1764 				continue;
1765 			}
1766 			if (Z_ISREF_P(entry)) {
1767 				Z_ADDREF_P(entry);
1768 			} else {
1769 				ZVAL_MAKE_REF_EX(entry, 2);
1770 			}
1771 			zval_ptr_dtor(orig_var);
1772 			ZVAL_REF(orig_var, Z_REF_P(entry));
1773 		} else {
1774 			if (Z_ISREF_P(entry)) {
1775 				Z_ADDREF_P(entry);
1776 			} else {
1777 				ZVAL_MAKE_REF_EX(entry, 2);
1778 			}
1779 			zend_hash_add_new(symbol_table, var_name, entry);
1780 		}
1781 		count++;
1782 	} ZEND_HASH_FOREACH_END();
1783 
1784 	return count;
1785 }
1786 /* }}} */
1787 
1788 static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1789 {
1790 	zend_long count = 0;
1791 	zend_string *var_name;
1792 	zval *entry, *orig_var;
1793 
1794 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1795 		if (!var_name) {
1796 			continue;
1797 		}
1798 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1799 			continue;
1800 		}
1801 		if (zend_string_equals_literal(var_name, "this")) {
1802 			zend_throw_error(NULL, "Cannot re-assign $this");
1803 			return -1;
1804 		}
1805 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1806 		if (orig_var) {
1807 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1808 				orig_var = Z_INDIRECT_P(orig_var);
1809 			}
1810 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1811 				continue;
1812 			}
1813 			ZVAL_DEREF(entry);
1814 			ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1815 			if (UNEXPECTED(EG(exception))) {
1816 				return -1;
1817 			}
1818 		} else {
1819 			ZVAL_DEREF(entry);
1820 			Z_TRY_ADDREF_P(entry);
1821 			zend_hash_add_new(symbol_table, var_name, entry);
1822 		}
1823 		count++;
1824 	} ZEND_HASH_FOREACH_END();
1825 
1826 	return count;
1827 }
1828 /* }}} */
1829 
1830 static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1831 {
1832 	zend_long count = 0;
1833 	zend_string *var_name;
1834 	zval *entry, *orig_var, final_name;
1835 
1836 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1837 		if (!var_name) {
1838 			continue;
1839 		}
1840 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1841 		if (orig_var) {
1842 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1843 				orig_var = Z_INDIRECT_P(orig_var);
1844 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1845 					if (Z_ISREF_P(entry)) {
1846 						Z_ADDREF_P(entry);
1847 					} else {
1848 						ZVAL_MAKE_REF_EX(entry, 2);
1849 					}
1850 					ZVAL_REF(orig_var, Z_REF_P(entry));
1851 					count++;
1852 					continue;
1853 				}
1854 			}
1855 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1856 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1857 				if (zend_string_equals_literal(Z_STR(final_name), "this")) {
1858 					zend_throw_error(NULL, "Cannot re-assign $this");
1859 					return -1;
1860 				} else {
1861 					if (Z_ISREF_P(entry)) {
1862 						Z_ADDREF_P(entry);
1863 					} else {
1864 						ZVAL_MAKE_REF_EX(entry, 2);
1865 					}
1866 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1867 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1868 							orig_var = Z_INDIRECT_P(orig_var);
1869 						}
1870 						zval_ptr_dtor(orig_var);
1871 						ZVAL_REF(orig_var, Z_REF_P(entry));
1872 					} else {
1873 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1874 					}
1875 					count++;
1876 				}
1877 			}
1878 			zval_ptr_dtor_str(&final_name);
1879 		}
1880 	} ZEND_HASH_FOREACH_END();
1881 
1882 	return count;
1883 }
1884 /* }}} */
1885 
1886 static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1887 {
1888 	zend_long count = 0;
1889 	zend_string *var_name;
1890 	zval *entry, *orig_var, final_name;
1891 
1892 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1893 		if (!var_name) {
1894 			continue;
1895 		}
1896 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1897 		if (orig_var) {
1898 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1899 				orig_var = Z_INDIRECT_P(orig_var);
1900 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1901 					ZVAL_COPY_DEREF(orig_var, entry);
1902 					count++;
1903 					continue;
1904 				}
1905 			}
1906 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1907 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1908 				if (zend_string_equals_literal(Z_STR(final_name), "this")) {
1909 					zend_throw_error(NULL, "Cannot re-assign $this");
1910 					return -1;
1911 				} else {
1912 					ZVAL_DEREF(entry);
1913 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1914 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1915 							orig_var = Z_INDIRECT_P(orig_var);
1916 						}
1917 						ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1918 						if (UNEXPECTED(EG(exception))) {
1919 							zend_string_release_ex(Z_STR(final_name), 0);
1920 							return -1;
1921 						}
1922 					} else {
1923 						Z_TRY_ADDREF_P(entry);
1924 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1925 					}
1926 					count++;
1927 				}
1928 			}
1929 			zval_ptr_dtor_str(&final_name);
1930 		}
1931 	} ZEND_HASH_FOREACH_END();
1932 
1933 	return count;
1934 }
1935 /* }}} */
1936 
1937 static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1938 {
1939 	zend_long count = 0;
1940 	zend_string *var_name;
1941 	zval *entry, *orig_var, final_name;
1942 
1943 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
1944 		if (!var_name) {
1945 			continue;
1946 		}
1947 		if (ZSTR_LEN(var_name) == 0) {
1948 			continue;
1949 		}
1950 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
1951 		if (orig_var) {
1952 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1953 				orig_var = Z_INDIRECT_P(orig_var);
1954 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1955 					if (Z_ISREF_P(entry)) {
1956 						Z_ADDREF_P(entry);
1957 					} else {
1958 						ZVAL_MAKE_REF_EX(entry, 2);
1959 					}
1960 					ZVAL_REF(orig_var, Z_REF_P(entry));
1961 					count++;
1962 					continue;
1963 				}
1964 			}
1965 prefix:
1966 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1967 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1968 				if (zend_string_equals_literal(Z_STR(final_name), "this")) {
1969 					zend_throw_error(NULL, "Cannot re-assign $this");
1970 					return -1;
1971 				} else {
1972 					if (Z_ISREF_P(entry)) {
1973 						Z_ADDREF_P(entry);
1974 					} else {
1975 						ZVAL_MAKE_REF_EX(entry, 2);
1976 					}
1977 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1978 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1979 							orig_var = Z_INDIRECT_P(orig_var);
1980 						}
1981 						zval_ptr_dtor(orig_var);
1982 						ZVAL_REF(orig_var, Z_REF_P(entry));
1983 					} else {
1984 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1985 					}
1986 					count++;
1987 				}
1988 			}
1989 			zval_ptr_dtor_str(&final_name);
1990 		} else {
1991 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1992 				continue;
1993 			}
1994 			if (zend_string_equals_literal(var_name, "this")) {
1995 				goto prefix;
1996 			}
1997 			if (Z_ISREF_P(entry)) {
1998 				Z_ADDREF_P(entry);
1999 			} else {
2000 				ZVAL_MAKE_REF_EX(entry, 2);
2001 			}
2002 			zend_hash_add_new(symbol_table, var_name, entry);
2003 			count++;
2004 		}
2005 	} ZEND_HASH_FOREACH_END();
2006 
2007 	return count;
2008 }
2009 /* }}} */
2010 
2011 static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2012 {
2013 	zend_long count = 0;
2014 	zend_string *var_name;
2015 	zval *entry, *orig_var, final_name;
2016 
2017 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
2018 		if (!var_name) {
2019 			continue;
2020 		}
2021 		if (ZSTR_LEN(var_name) == 0) {
2022 			continue;
2023 		}
2024 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
2025 		if (orig_var) {
2026 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2027 				orig_var = Z_INDIRECT_P(orig_var);
2028 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2029 					ZVAL_COPY_DEREF(orig_var, entry);
2030 					count++;
2031 					continue;
2032 				}
2033 			}
2034 prefix:
2035 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2036 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2037 				if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2038 					zend_throw_error(NULL, "Cannot re-assign $this");
2039 					return -1;
2040 				} else {
2041 					ZVAL_DEREF(entry);
2042 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2043 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2044 							orig_var = Z_INDIRECT_P(orig_var);
2045 						}
2046 						ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2047 						if (UNEXPECTED(EG(exception))) {
2048 							zend_string_release_ex(Z_STR(final_name), 0);
2049 							return -1;
2050 						}
2051 					} else {
2052 						Z_TRY_ADDREF_P(entry);
2053 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2054 					}
2055 					count++;
2056 				}
2057 			}
2058 			zval_ptr_dtor_str(&final_name);
2059 		} else {
2060 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2061 				continue;
2062 			}
2063 			if (zend_string_equals_literal(var_name, "this")) {
2064 				goto prefix;
2065 			}
2066 			ZVAL_DEREF(entry);
2067 			Z_TRY_ADDREF_P(entry);
2068 			zend_hash_add_new(symbol_table, var_name, entry);
2069 			count++;
2070 		}
2071 	} ZEND_HASH_FOREACH_END();
2072 
2073 	return count;
2074 }
2075 /* }}} */
2076 
2077 static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2078 {
2079 	zend_long count = 0;
2080 	zend_string *var_name;
2081 	zend_ulong num_key;
2082 	zval *entry, *orig_var, final_name;
2083 
2084 	ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
2085 		if (var_name) {
2086 			if (ZSTR_LEN(var_name) == 0) {
2087 				continue;
2088 			}
2089 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2090 		} else {
2091 			zend_string *str = zend_long_to_str(num_key);
2092 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2093 			zend_string_release_ex(str, 0);
2094 		}
2095 		if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2096 			if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2097 				zend_throw_error(NULL, "Cannot re-assign $this");
2098 				return -1;
2099 			} else {
2100 				if (Z_ISREF_P(entry)) {
2101 					Z_ADDREF_P(entry);
2102 				} else {
2103 					ZVAL_MAKE_REF_EX(entry, 2);
2104 				}
2105 				if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2106 					if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2107 						orig_var = Z_INDIRECT_P(orig_var);
2108 					}
2109 					zval_ptr_dtor(orig_var);
2110 					ZVAL_REF(orig_var, Z_REF_P(entry));
2111 				} else {
2112 					zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2113 				}
2114 				count++;
2115 			}
2116 		}
2117 		zval_ptr_dtor_str(&final_name);
2118 	} ZEND_HASH_FOREACH_END();
2119 
2120 	return count;
2121 }
2122 /* }}} */
2123 
2124 static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2125 {
2126 	zend_long count = 0;
2127 	zend_string *var_name;
2128 	zend_ulong num_key;
2129 	zval *entry, *orig_var, final_name;
2130 
2131 	ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
2132 		if (var_name) {
2133 			if (ZSTR_LEN(var_name) == 0) {
2134 				continue;
2135 			}
2136 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2137 		} else {
2138 			zend_string *str = zend_long_to_str(num_key);
2139 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2140 			zend_string_release_ex(str, 0);
2141 		}
2142 		if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2143 			if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2144 				zend_throw_error(NULL, "Cannot re-assign $this");
2145 				return -1;
2146 			} else {
2147 				ZVAL_DEREF(entry);
2148 				if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2149 					if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2150 						orig_var = Z_INDIRECT_P(orig_var);
2151 					}
2152 					ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2153 					if (UNEXPECTED(EG(exception))) {
2154 						zend_string_release_ex(Z_STR(final_name), 0);
2155 						return -1;
2156 					}
2157 				} else {
2158 					Z_TRY_ADDREF_P(entry);
2159 					zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2160 				}
2161 				count++;
2162 			}
2163 		}
2164 		zval_ptr_dtor_str(&final_name);
2165 	} ZEND_HASH_FOREACH_END();
2166 
2167 	return count;
2168 }
2169 /* }}} */
2170 
2171 static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2172 {
2173 	zend_long count = 0;
2174 	zend_string *var_name;
2175 	zend_ulong num_key;
2176 	zval *entry, *orig_var, final_name;
2177 
2178 	ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
2179 		if (var_name) {
2180 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2181 			 || zend_string_equals_literal(var_name, "this")) {
2182 				php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2183 				if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2184 					zval_ptr_dtor_str(&final_name);
2185 					continue;
2186 				}
2187 			} else {
2188 				ZVAL_STR_COPY(&final_name, var_name);
2189 			}
2190 		} else {
2191 			zend_string *str = zend_long_to_str(num_key);
2192 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2193 			zend_string_release_ex(str, 0);
2194 			if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2195 				zval_ptr_dtor_str(&final_name);
2196 				continue;
2197 			}
2198 		}
2199 		if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2200 			zend_throw_error(NULL, "Cannot re-assign $this");
2201 			return -1;
2202 		} else {
2203 			if (Z_ISREF_P(entry)) {
2204 				Z_ADDREF_P(entry);
2205 			} else {
2206 				ZVAL_MAKE_REF_EX(entry, 2);
2207 			}
2208 			if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2209 				if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2210 					orig_var = Z_INDIRECT_P(orig_var);
2211 				}
2212 				zval_ptr_dtor(orig_var);
2213 				ZVAL_REF(orig_var, Z_REF_P(entry));
2214 			} else {
2215 				zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2216 			}
2217 			count++;
2218 		}
2219 		zval_ptr_dtor_str(&final_name);
2220 	} ZEND_HASH_FOREACH_END();
2221 
2222 	return count;
2223 }
2224 /* }}} */
2225 
2226 static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2227 {
2228 	zend_long count = 0;
2229 	zend_string *var_name;
2230 	zend_ulong num_key;
2231 	zval *entry, *orig_var, final_name;
2232 
2233 	ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
2234 		if (var_name) {
2235 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2236 			 || zend_string_equals_literal(var_name, "this")) {
2237 				php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2238 				if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2239 					zval_ptr_dtor_str(&final_name);
2240 					continue;
2241 				}
2242 			} else {
2243 				ZVAL_STR_COPY(&final_name, var_name);
2244 			}
2245 		} else {
2246 			zend_string *str = zend_long_to_str(num_key);
2247 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2248 			zend_string_release_ex(str, 0);
2249 			if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2250 				zval_ptr_dtor_str(&final_name);
2251 				continue;
2252 			}
2253 		}
2254 		if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2255 			zend_throw_error(NULL, "Cannot re-assign $this");
2256 			return -1;
2257 		} else {
2258 			ZVAL_DEREF(entry);
2259 			if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2260 				if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2261 					orig_var = Z_INDIRECT_P(orig_var);
2262 				}
2263 				ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2264 				if (UNEXPECTED(EG(exception))) {
2265 					zend_string_release_ex(Z_STR(final_name), 0);
2266 					return -1;
2267 				}
2268 			} else {
2269 				Z_TRY_ADDREF_P(entry);
2270 				zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2271 			}
2272 			count++;
2273 		}
2274 		zval_ptr_dtor_str(&final_name);
2275 	} ZEND_HASH_FOREACH_END();
2276 
2277 	return count;
2278 }
2279 /* }}} */
2280 
2281 static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2282 {
2283 	zend_long count = 0;
2284 	zend_string *var_name;
2285 	zval *entry, *orig_var;
2286 
2287 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
2288 		if (!var_name) {
2289 			continue;
2290 		}
2291 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2292 			continue;
2293 		}
2294 		if (zend_string_equals_literal(var_name, "this")) {
2295 			continue;
2296 		}
2297 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
2298 		if (orig_var) {
2299 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2300 				orig_var = Z_INDIRECT_P(orig_var);
2301 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2302 					if (Z_ISREF_P(entry)) {
2303 						Z_ADDREF_P(entry);
2304 					} else {
2305 						ZVAL_MAKE_REF_EX(entry, 2);
2306 					}
2307 					ZVAL_REF(orig_var, Z_REF_P(entry));
2308 					count++;
2309 				}
2310 			}
2311 		} else {
2312 			if (Z_ISREF_P(entry)) {
2313 				Z_ADDREF_P(entry);
2314 			} else {
2315 				ZVAL_MAKE_REF_EX(entry, 2);
2316 			}
2317 			zend_hash_add_new(symbol_table, var_name, entry);
2318 			count++;
2319 		}
2320 	} ZEND_HASH_FOREACH_END();
2321 
2322 	return count;
2323 }
2324 /* }}} */
2325 
2326 static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2327 {
2328 	zend_long count = 0;
2329 	zend_string *var_name;
2330 	zval *entry, *orig_var;
2331 
2332 	ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
2333 		if (!var_name) {
2334 			continue;
2335 		}
2336 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2337 			continue;
2338 		}
2339 		if (zend_string_equals_literal(var_name, "this")) {
2340 			continue;
2341 		}
2342 		orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
2343 		if (orig_var) {
2344 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2345 				orig_var = Z_INDIRECT_P(orig_var);
2346 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2347 					ZVAL_COPY_DEREF(orig_var, entry);
2348 					count++;
2349 				}
2350 			}
2351 		} else {
2352 			ZVAL_DEREF(entry);
2353 			Z_TRY_ADDREF_P(entry);
2354 			zend_hash_add_new(symbol_table, var_name, entry);
2355 			count++;
2356 		}
2357 	} ZEND_HASH_FOREACH_END();
2358 
2359 	return count;
2360 }
2361 /* }}} */
2362 
2363 /* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
2364    Imports variables into symbol table from an array */
2365 PHP_FUNCTION(extract)
2366 {
2367 	zval *var_array_param;
2368 	zend_long extract_refs;
2369 	zend_long extract_type = EXTR_OVERWRITE;
2370 	zend_string *prefix = NULL;
2371 	zend_long count;
2372 	zend_array *symbol_table;
2373 
2374 	ZEND_PARSE_PARAMETERS_START(1, 3)
2375 		Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2376 		Z_PARAM_OPTIONAL
2377 		Z_PARAM_LONG(extract_type)
2378 		Z_PARAM_STR(prefix)
2379 	ZEND_PARSE_PARAMETERS_END();
2380 
2381 	extract_refs = (extract_type & EXTR_REFS);
2382 	if (extract_refs) {
2383 		SEPARATE_ARRAY(var_array_param);
2384 	}
2385 	extract_type &= 0xff;
2386 
2387 	if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
2388 		zend_argument_value_error(2, "must be a valid extract type");
2389 		RETURN_THROWS();
2390 	}
2391 
2392 	if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2393 		zend_argument_value_error(3, "is required when using this extract type");
2394 		RETURN_THROWS();
2395 	}
2396 
2397 	if (prefix) {
2398 		if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
2399 			zend_argument_value_error(3, "must be a valid identifier");
2400 			RETURN_THROWS();
2401 		}
2402 	}
2403 
2404 	if (zend_forbid_dynamic_call("extract()") == FAILURE) {
2405 		return;
2406 	}
2407 
2408 	symbol_table = zend_rebuild_symbol_table();
2409 	ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2410 
2411 	if (extract_refs) {
2412 		switch (extract_type) {
2413 			case EXTR_IF_EXISTS:
2414 				count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2415 				break;
2416 			case EXTR_OVERWRITE:
2417 				count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2418 				break;
2419 			case EXTR_PREFIX_IF_EXISTS:
2420 				count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2421 				break;
2422 			case EXTR_PREFIX_SAME:
2423 				count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2424 				break;
2425 			case EXTR_PREFIX_ALL:
2426 				count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2427 				break;
2428 			case EXTR_PREFIX_INVALID:
2429 				count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2430 				break;
2431 			default:
2432 				count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2433 				break;
2434 		}
2435 	} else {
2436 		/* The array might be stored in a local variable that will be overwritten */
2437 		zval array_copy;
2438 		ZVAL_COPY(&array_copy, var_array_param);
2439 		switch (extract_type) {
2440 			case EXTR_IF_EXISTS:
2441 				count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2442 				break;
2443 			case EXTR_OVERWRITE:
2444 				count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2445 				break;
2446 			case EXTR_PREFIX_IF_EXISTS:
2447 				count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2448 				break;
2449 			case EXTR_PREFIX_SAME:
2450 				count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2451 				break;
2452 			case EXTR_PREFIX_ALL:
2453 				count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2454 				break;
2455 			case EXTR_PREFIX_INVALID:
2456 				count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2457 				break;
2458 			default:
2459 				count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2460 				break;
2461 		}
2462 		zval_ptr_dtor(&array_copy);
2463 	}
2464 
2465 	RETURN_LONG(count);
2466 }
2467 /* }}} */
2468 
2469 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry) /* {{{ */
2470 {
2471 	zval *value_ptr, data;
2472 
2473 	ZVAL_DEREF(entry);
2474 	if (Z_TYPE_P(entry) == IS_STRING) {
2475 		if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2476 			ZVAL_DEREF(value_ptr);
2477 			Z_TRY_ADDREF_P(value_ptr);
2478 			zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2479 		} else if (zend_string_equals_literal(Z_STR_P(entry), "this")) {
2480 			zend_object *object = zend_get_this_object(EG(current_execute_data));
2481 			if (object) {
2482 				GC_ADDREF(object);
2483 				ZVAL_OBJ(&data, object);
2484 				zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2485 			}
2486 		} else {
2487 			php_error_docref(NULL, E_NOTICE, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
2488 		}
2489 	} else if (Z_TYPE_P(entry) == IS_ARRAY) {
2490 	    if (Z_REFCOUNTED_P(entry)) {
2491 			if (Z_IS_RECURSIVE_P(entry)) {
2492 				zend_throw_error(NULL, "Recursion detected");
2493 				return;
2494 			}
2495 			Z_PROTECT_RECURSION_P(entry);
2496 		}
2497 		ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
2498 			php_compact_var(eg_active_symbol_table, return_value, value_ptr);
2499 		} ZEND_HASH_FOREACH_END();
2500 	    if (Z_REFCOUNTED_P(entry)) {
2501 			Z_UNPROTECT_RECURSION_P(entry);
2502 		}
2503 	}
2504 }
2505 /* }}} */
2506 
2507 /* {{{ proto array compact(mixed var_names [, mixed ...])
2508    Creates a hash containing variables and their values */
2509 PHP_FUNCTION(compact)
2510 {
2511 	zval *args = NULL;	/* function arguments array */
2512 	uint32_t num_args, i;
2513 	zend_array *symbol_table;
2514 
2515 	ZEND_PARSE_PARAMETERS_START(1, -1)
2516 		Z_PARAM_VARIADIC('+', args, num_args)
2517 	ZEND_PARSE_PARAMETERS_END();
2518 
2519 	if (zend_forbid_dynamic_call("compact()") == FAILURE) {
2520 		return;
2521 	}
2522 
2523 	symbol_table = zend_rebuild_symbol_table();
2524 	ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2525 
2526 	/* compact() is probably most used with a single array of var_names
2527 	   or multiple string names, rather than a combination of both.
2528 	   So quickly guess a minimum result size based on that */
2529 	if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2530 		array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2531 	} else {
2532 		array_init_size(return_value, num_args);
2533 	}
2534 
2535 	for (i = 0; i < num_args; i++) {
2536 		php_compact_var(symbol_table, return_value, &args[i]);
2537 	}
2538 }
2539 /* }}} */
2540 
2541 /* {{{ proto array array_fill(int start_key, int num, mixed val)
2542    Create an array containing num elements starting with index start_key each initialized to val */
2543 PHP_FUNCTION(array_fill)
2544 {
2545 	zval *val;
2546 	zend_long start_key, num;
2547 
2548 	ZEND_PARSE_PARAMETERS_START(3, 3)
2549 		Z_PARAM_LONG(start_key)
2550 		Z_PARAM_LONG(num)
2551 		Z_PARAM_ZVAL(val)
2552 	ZEND_PARSE_PARAMETERS_END();
2553 
2554 	if (EXPECTED(num > 0)) {
2555 		if (sizeof(num) > 4 && UNEXPECTED(EXPECTED(num > 0x7fffffff))) {
2556 			zend_argument_value_error(2, "is too large");
2557 			RETURN_THROWS();
2558 		} else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2559 			zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2560 			RETURN_THROWS();
2561 		} else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2562 			/* create packed array */
2563 			Bucket *p;
2564 			zend_long n;
2565 
2566 			array_init_size(return_value, (uint32_t)(start_key + num));
2567 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2568 			Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2569 			Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2570 			Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2571 
2572 			if (Z_REFCOUNTED_P(val)) {
2573 				GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2574 			}
2575 
2576 			p = Z_ARRVAL_P(return_value)->arData;
2577 			n = start_key;
2578 
2579 			while (start_key--) {
2580 				ZVAL_UNDEF(&p->val);
2581 				p++;
2582 			}
2583 			while (num--) {
2584 				ZVAL_COPY_VALUE(&p->val, val);
2585 				p->h = n++;
2586 				p->key = NULL;
2587 				p++;
2588 			}
2589 		} else {
2590 			/* create hash */
2591 			array_init_size(return_value, (uint32_t)num);
2592 			zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2593 			if (Z_REFCOUNTED_P(val)) {
2594 				GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2595 			}
2596 			zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2597 			while (--num) {
2598 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2599 				start_key++;
2600 			}
2601 		}
2602 	} else if (EXPECTED(num == 0)) {
2603 		RETURN_EMPTY_ARRAY();
2604 	} else {
2605 		zend_argument_value_error(2, "must be greater than or equal to 0");
2606 		RETURN_THROWS();
2607 	}
2608 }
2609 /* }}} */
2610 
2611 /* {{{ proto array array_fill_keys(array keys, mixed val)
2612    Create an array using the elements of the first parameter as keys each initialized to val */
2613 PHP_FUNCTION(array_fill_keys)
2614 {
2615 	zval *keys, *val, *entry;
2616 
2617 	ZEND_PARSE_PARAMETERS_START(2, 2)
2618 		Z_PARAM_ARRAY(keys)
2619 		Z_PARAM_ZVAL(val)
2620 	ZEND_PARSE_PARAMETERS_END();
2621 
2622 	/* Initialize return array */
2623 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2624 
2625 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2626 		ZVAL_DEREF(entry);
2627 		Z_TRY_ADDREF_P(val);
2628 		if (Z_TYPE_P(entry) == IS_LONG) {
2629 			zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2630 		} else {
2631 			zend_string *tmp_key;
2632 			zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2633 			zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2634 			zend_tmp_string_release(tmp_key);
2635 		}
2636 	} ZEND_HASH_FOREACH_END();
2637 }
2638 /* }}} */
2639 
2640 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
2641 		double __calc_size = ((start - end) / step) + 1; \
2642 		if (__calc_size >= (double)HT_MAX_SIZE) { \
2643 			zend_value_error(\
2644 					"The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
2645 			RETURN_THROWS(); \
2646 		} \
2647 		size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2648 		array_init_size(return_value, size); \
2649 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2650 	} while (0)
2651 
2652 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
2653 		zend_ulong __calc_size = ((zend_ulong) start - end) / lstep; \
2654 		if (__calc_size >= HT_MAX_SIZE - 1) { \
2655 			zend_value_error(\
2656 					"The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
2657 			RETURN_THROWS(); \
2658 		} \
2659 		size = (uint32_t)(__calc_size + 1); \
2660 		array_init_size(return_value, size); \
2661 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2662 	} while (0)
2663 
2664 /* {{{ proto array range(mixed low, mixed high[, int step])
2665    Create an array containing the range of integers or characters from low to high (inclusive) */
2666 PHP_FUNCTION(range)
2667 {
2668 	zval *zlow, *zhigh, *zstep = NULL, tmp;
2669 	int err = 0, is_step_double = 0;
2670 	double step = 1.0;
2671 
2672 	ZEND_PARSE_PARAMETERS_START(2, 3)
2673 		Z_PARAM_ZVAL(zlow)
2674 		Z_PARAM_ZVAL(zhigh)
2675 		Z_PARAM_OPTIONAL
2676 		Z_PARAM_NUMBER(zstep)
2677 	ZEND_PARSE_PARAMETERS_END();
2678 
2679 	if (zstep) {
2680 		is_step_double = Z_TYPE_P(zstep) == IS_DOUBLE;
2681 		step = zval_get_double(zstep);
2682 
2683 		/* We only want positive step values. */
2684 		if (step < 0.0) {
2685 			step *= -1;
2686 		}
2687 	}
2688 
2689 	/* If the range is given as strings, generate an array of characters. */
2690 	if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
2691 		int type1, type2;
2692 		unsigned char low, high;
2693 		zend_long lstep = (zend_long) step;
2694 
2695 		type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
2696 		type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
2697 
2698 		if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
2699 			goto double_str;
2700 		} else if (type1 == IS_LONG || type2 == IS_LONG) {
2701 			goto long_str;
2702 		}
2703 
2704 		low = (unsigned char)Z_STRVAL_P(zlow)[0];
2705 		high = (unsigned char)Z_STRVAL_P(zhigh)[0];
2706 
2707 		if (low > high) {		/* Negative Steps */
2708 			if (lstep <= 0) {
2709 				err = 1;
2710 				goto err;
2711 			}
2712 			/* Initialize the return_value as an array. */
2713 			array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1));
2714 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2715 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2716 				for (; low >= high; low -= (unsigned int)lstep) {
2717 					ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2718 					ZEND_HASH_FILL_NEXT();
2719 					if (((signed int)low - lstep) < 0) {
2720 						break;
2721 					}
2722 				}
2723 			} ZEND_HASH_FILL_END();
2724 		} else if (high > low) {	/* Positive Steps */
2725 			if (lstep <= 0) {
2726 				err = 1;
2727 				goto err;
2728 			}
2729 			array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1));
2730 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2731 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2732 				for (; low <= high; low += (unsigned int)lstep) {
2733 					ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2734 					ZEND_HASH_FILL_NEXT();
2735 					if (((signed int)low + lstep) > 255) {
2736 						break;
2737 					}
2738 				}
2739 			} ZEND_HASH_FILL_END();
2740 		} else {
2741 			array_init(return_value);
2742 			ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR(low));
2743 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2744 		}
2745 	} else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
2746 		double low, high, element;
2747 		uint32_t i, size;
2748 double_str:
2749 		low = zval_get_double(zlow);
2750 		high = zval_get_double(zhigh);
2751 
2752 		if (zend_isinf(high) || zend_isinf(low)) {
2753 			zend_value_error("Invalid range supplied: start=%0.0f end=%0.0f", low, high);
2754 			RETURN_THROWS();
2755 		}
2756 
2757 		if (low > high) { 		/* Negative steps */
2758 			if (low - high < step || step <= 0) {
2759 				err = 1;
2760 				goto err;
2761 			}
2762 
2763 			RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
2764 
2765 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2766 				for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step)) {
2767 					ZEND_HASH_FILL_SET_DOUBLE(element);
2768 					ZEND_HASH_FILL_NEXT();
2769 				}
2770 			} ZEND_HASH_FILL_END();
2771 		} else if (high > low) { 	/* Positive steps */
2772 			if (high - low < step || step <= 0) {
2773 				err = 1;
2774 				goto err;
2775 			}
2776 
2777 			RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
2778 
2779 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2780 				for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step)) {
2781 					ZEND_HASH_FILL_SET_DOUBLE(element);
2782 					ZEND_HASH_FILL_NEXT();
2783 				}
2784 			} ZEND_HASH_FILL_END();
2785 		} else {
2786 			array_init(return_value);
2787 			ZVAL_DOUBLE(&tmp, low);
2788 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2789 		}
2790 	} else {
2791 		zend_long low, high;
2792 		/* lstep is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
2793 		zend_ulong lstep;
2794 		uint32_t i, size;
2795 long_str:
2796 		low = zval_get_long(zlow);
2797 		high = zval_get_long(zhigh);
2798 
2799 		if (step <= 0) {
2800 			err = 1;
2801 			goto err;
2802 		}
2803 
2804 		lstep = (zend_ulong)step;
2805 		if (step <= 0) {
2806 			err = 1;
2807 			goto err;
2808 		}
2809 
2810 		if (low > high) { 		/* Negative steps */
2811 			if ((zend_ulong)low - high < lstep) {
2812 				err = 1;
2813 				goto err;
2814 			}
2815 
2816 			RANGE_CHECK_LONG_INIT_ARRAY(low, high);
2817 
2818 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2819 				for (i = 0; i < size; ++i) {
2820 					ZEND_HASH_FILL_SET_LONG(low - (i * lstep));
2821 					ZEND_HASH_FILL_NEXT();
2822 				}
2823 			} ZEND_HASH_FILL_END();
2824 		} else if (high > low) { 	/* Positive steps */
2825 			if ((zend_ulong)high - low < lstep) {
2826 				err = 1;
2827 				goto err;
2828 			}
2829 
2830 			RANGE_CHECK_LONG_INIT_ARRAY(high, low);
2831 
2832 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2833 				for (i = 0; i < size; ++i) {
2834 					ZEND_HASH_FILL_SET_LONG(low + (i * lstep));
2835 					ZEND_HASH_FILL_NEXT();
2836 				}
2837 			} ZEND_HASH_FILL_END();
2838 		} else {
2839 			array_init(return_value);
2840 			ZVAL_LONG(&tmp, low);
2841 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2842 		}
2843 	}
2844 err:
2845 	if (err) {
2846 		zend_argument_value_error(3, "must not exceed the specified range");
2847 		RETURN_THROWS();
2848 	}
2849 }
2850 /* }}} */
2851 
2852 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
2853 #undef RANGE_CHECK_LONG_INIT_ARRAY
2854 
2855 static void php_array_data_shuffle(zval *array) /* {{{ */
2856 {
2857 	uint32_t idx, j, n_elems;
2858 	Bucket *p, temp;
2859 	HashTable *hash;
2860 	zend_long rnd_idx;
2861 	uint32_t n_left;
2862 
2863 	n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
2864 
2865 	if (n_elems < 1) {
2866 		return;
2867 	}
2868 
2869 	hash = Z_ARRVAL_P(array);
2870 	n_left = n_elems;
2871 
2872 	if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
2873 		if (hash->nNumUsed != hash->nNumOfElements) {
2874 			for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2875 				p = hash->arData + idx;
2876 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
2877 				if (j != idx) {
2878 					hash->arData[j] = *p;
2879 				}
2880 				j++;
2881 			}
2882 		}
2883 		while (--n_left) {
2884 			rnd_idx = php_mt_rand_range(0, n_left);
2885 			if (rnd_idx != n_left) {
2886 				temp = hash->arData[n_left];
2887 				hash->arData[n_left] = hash->arData[rnd_idx];
2888 				hash->arData[rnd_idx] = temp;
2889 			}
2890 		}
2891 	} else {
2892 		uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
2893 
2894 		if (hash->nNumUsed != hash->nNumOfElements) {
2895 			for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2896 				p = hash->arData + idx;
2897 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
2898 				if (j != idx) {
2899 					hash->arData[j] = *p;
2900 					if (idx == iter_pos) {
2901 						zend_hash_iterators_update(hash, idx, j);
2902 						iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
2903 					}
2904 				}
2905 				j++;
2906 			}
2907 		}
2908 		while (--n_left) {
2909 			rnd_idx = php_mt_rand_range(0, n_left);
2910 			if (rnd_idx != n_left) {
2911 				temp = hash->arData[n_left];
2912 				hash->arData[n_left] = hash->arData[rnd_idx];
2913 				hash->arData[rnd_idx] = temp;
2914 				zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
2915 			}
2916 		}
2917 	}
2918 	hash->nNumUsed = n_elems;
2919 	hash->nInternalPointer = 0;
2920 
2921 	for (j = 0; j < n_elems; j++) {
2922 		p = hash->arData + j;
2923 		if (p->key) {
2924 			zend_string_release_ex(p->key, 0);
2925 		}
2926 		p->h = j;
2927 		p->key = NULL;
2928 	}
2929 	hash->nNextFreeElement = n_elems;
2930 	if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
2931 		zend_hash_to_packed(hash);
2932 	}
2933 }
2934 /* }}} */
2935 
2936 /* {{{ proto bool shuffle(array array_arg)
2937    Randomly shuffle the contents of an array */
2938 PHP_FUNCTION(shuffle)
2939 {
2940 	zval *array;
2941 
2942 	ZEND_PARSE_PARAMETERS_START(1, 1)
2943 		Z_PARAM_ARRAY_EX(array, 0, 1)
2944 	ZEND_PARSE_PARAMETERS_END();
2945 
2946 	php_array_data_shuffle(array);
2947 
2948 	RETURN_TRUE;
2949 }
2950 /* }}} */
2951 
2952 static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
2953 {
2954 	HashTable 	 out_hash;			/* Output hashtable */
2955 	zend_long	 num_in;			/* Number of entries in the input hashtable */
2956 	zend_long	 pos;				/* Current position in the hashtable */
2957 	uint32_t     idx;
2958 	Bucket		*p;					/* Pointer to hash bucket */
2959 	zval		*entry;				/* Hash entry */
2960 	uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
2961 
2962 	/* Get number of entries in the input hash */
2963 	num_in = zend_hash_num_elements(in_hash);
2964 
2965 	/* Clamp the offset.. */
2966 	if (offset > num_in) {
2967 		offset = num_in;
2968 	} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2969 		offset = 0;
2970 	}
2971 
2972 	/* ..and the length */
2973 	if (length < 0) {
2974 		length = num_in - offset + length;
2975 	} else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
2976 		length = num_in - offset;
2977 	}
2978 
2979 	/* Create and initialize output hash */
2980 	zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
2981 
2982 	/* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
2983 	for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
2984 		p = in_hash->arData + idx;
2985 		if (Z_TYPE(p->val) == IS_UNDEF) continue;
2986 		/* Get entry and increase reference count */
2987 		entry = &p->val;
2988 
2989 		/* Update output hash depending on key type */
2990 		if (p->key == NULL) {
2991 			zend_hash_next_index_insert_new(&out_hash, entry);
2992 		} else {
2993 			zend_hash_add_new(&out_hash, p->key, entry);
2994 		}
2995 		if (idx == iter_pos) {
2996 			if ((zend_long)idx != pos) {
2997 				zend_hash_iterators_update(in_hash, idx, pos);
2998 			}
2999 			iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3000 		}
3001 		pos++;
3002 	}
3003 
3004 	/* If hash for removed entries exists, go until offset+length and copy the entries to it */
3005 	if (removed != NULL) {
3006 		for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
3007 			p = in_hash->arData + idx;
3008 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
3009 			pos++;
3010 			entry = &p->val;
3011 			Z_TRY_ADDREF_P(entry);
3012 			if (p->key == NULL) {
3013 				zend_hash_next_index_insert_new(removed, entry);
3014 				zend_hash_del_bucket(in_hash, p);
3015 			} else {
3016 				zend_hash_add_new(removed, p->key, entry);
3017 				if (in_hash == &EG(symbol_table)) {
3018 					zend_delete_global_variable(p->key);
3019 				} else {
3020 					zend_hash_del_bucket(in_hash, p);
3021 				}
3022 			}
3023 		}
3024 	} else { /* otherwise just skip those entries */
3025 		int pos2 = pos;
3026 
3027 		for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
3028 			p = in_hash->arData + idx;
3029 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
3030 			pos2++;
3031 			if (p->key && in_hash == &EG(symbol_table)) {
3032 				zend_delete_global_variable(p->key);
3033 			} else {
3034 				zend_hash_del_bucket(in_hash, p);
3035 			}
3036 		}
3037 	}
3038 	iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3039 
3040 	/* If there are entries to insert.. */
3041 	if (replace) {
3042 		ZEND_HASH_FOREACH_VAL_IND(replace, entry) {
3043 			Z_TRY_ADDREF_P(entry);
3044 			zend_hash_next_index_insert_new(&out_hash, entry);
3045 			pos++;
3046 		} ZEND_HASH_FOREACH_END();
3047 	}
3048 
3049 	/* Copy the remaining input hash entries to the output hash */
3050 	for ( ; idx < in_hash->nNumUsed ; idx++) {
3051 		p = in_hash->arData + idx;
3052 		if (Z_TYPE(p->val) == IS_UNDEF) continue;
3053 		entry = &p->val;
3054 		if (p->key == NULL) {
3055 			zend_hash_next_index_insert_new(&out_hash, entry);
3056 		} else {
3057 			zend_hash_add_new(&out_hash, p->key, entry);
3058 		}
3059 		if (idx == iter_pos) {
3060 			if ((zend_long)idx != pos) {
3061 				zend_hash_iterators_update(in_hash, idx, pos);
3062 			}
3063 			iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3064 		}
3065 		pos++;
3066 	}
3067 
3068 	/* replace HashTable data */
3069 	HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3070 	HT_SET_ITERATORS_COUNT(in_hash, 0);
3071 	in_hash->pDestructor = NULL;
3072 	zend_hash_destroy(in_hash);
3073 
3074 	HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
3075 	in_hash->nTableSize        = out_hash.nTableSize;
3076 	in_hash->nTableMask        = out_hash.nTableMask;
3077 	in_hash->nNumUsed          = out_hash.nNumUsed;
3078 	in_hash->nNumOfElements    = out_hash.nNumOfElements;
3079 	in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
3080 	in_hash->arData            = out_hash.arData;
3081 	in_hash->pDestructor       = out_hash.pDestructor;
3082 
3083 	zend_hash_internal_pointer_reset(in_hash);
3084 }
3085 /* }}} */
3086 
3087 /* {{{ proto int array_push(array stack, mixed var [, mixed ...])
3088    Pushes elements onto the end of the array */
3089 PHP_FUNCTION(array_push)
3090 {
3091 	zval   *args,		/* Function arguments array */
3092 		   *stack,		/* Input array */
3093 		    new_var;	/* Variable to be pushed */
3094 	int i,				/* Loop counter */
3095 		argc;			/* Number of function arguments */
3096 
3097 
3098 	ZEND_PARSE_PARAMETERS_START(1, -1)
3099 		Z_PARAM_ARRAY_EX(stack, 0, 1)
3100 		Z_PARAM_VARIADIC('+', args, argc)
3101 	ZEND_PARSE_PARAMETERS_END();
3102 
3103 	/* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3104 	for (i = 0; i < argc; i++) {
3105 		ZVAL_COPY(&new_var, &args[i]);
3106 
3107 		if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
3108 			Z_TRY_DELREF(new_var);
3109 			zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3110 			RETURN_THROWS();
3111 		}
3112 	}
3113 
3114 	/* Clean up and return the number of values in the stack */
3115 	RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3116 }
3117 /* }}} */
3118 
3119 /* {{{ proto mixed array_pop(array stack)
3120    Pops an element off the end of the array */
3121 PHP_FUNCTION(array_pop)
3122 {
3123 	zval *stack,	/* Input stack */
3124 		 *val;		/* Value to be popped */
3125 	uint32_t idx;
3126 	Bucket *p;
3127 
3128 	ZEND_PARSE_PARAMETERS_START(1, 1)
3129 		Z_PARAM_ARRAY_EX(stack, 0, 1)
3130 	ZEND_PARSE_PARAMETERS_END();
3131 
3132 	if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3133 		return;
3134 	}
3135 
3136 	/* Get the last value and copy it into the return value */
3137 	idx = Z_ARRVAL_P(stack)->nNumUsed;
3138 	while (1) {
3139 		if (idx == 0) {
3140 			return;
3141 		}
3142 		idx--;
3143 		p = Z_ARRVAL_P(stack)->arData + idx;
3144 		val = &p->val;
3145 		if (Z_TYPE_P(val) == IS_INDIRECT) {
3146 			val = Z_INDIRECT_P(val);
3147 		}
3148 		if (Z_TYPE_P(val) != IS_UNDEF) {
3149 			break;
3150 		}
3151 	}
3152 	ZVAL_COPY_DEREF(return_value, val);
3153 
3154 	if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3155 		Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3156 	}
3157 
3158 	/* Delete the last value */
3159 	if (p->key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
3160 		zend_delete_global_variable(p->key);
3161 	} else {
3162 		zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3163 	}
3164 
3165 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3166 }
3167 /* }}} */
3168 
3169 /* {{{ proto mixed array_shift(array stack)
3170    Pops an element off the beginning of the array */
3171 PHP_FUNCTION(array_shift)
3172 {
3173 	zval *stack,	/* Input stack */
3174 		 *val;		/* Value to be popped */
3175 	uint32_t idx;
3176 	Bucket *p;
3177 
3178 	ZEND_PARSE_PARAMETERS_START(1, 1)
3179 		Z_PARAM_ARRAY_EX(stack, 0, 1)
3180 	ZEND_PARSE_PARAMETERS_END();
3181 
3182 	if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3183 		return;
3184 	}
3185 
3186 	/* Get the first value and copy it into the return value */
3187 	idx = 0;
3188 	while (1) {
3189 		if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3190 			return;
3191 		}
3192 		p = Z_ARRVAL_P(stack)->arData + idx;
3193 		val = &p->val;
3194 		if (Z_TYPE_P(val) == IS_INDIRECT) {
3195 			val = Z_INDIRECT_P(val);
3196 		}
3197 		if (Z_TYPE_P(val) != IS_UNDEF) {
3198 			break;
3199 		}
3200 		idx++;
3201 	}
3202 	ZVAL_COPY_DEREF(return_value, val);
3203 
3204 	/* Delete the first value */
3205 	if (p->key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
3206 		zend_delete_global_variable(p->key);
3207 	} else {
3208 		zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3209 	}
3210 
3211 	/* re-index like it did before */
3212 	if (HT_FLAGS(Z_ARRVAL_P(stack)) & HASH_FLAG_PACKED) {
3213 		uint32_t k = 0;
3214 
3215 		if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3216 			for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3217 				p = Z_ARRVAL_P(stack)->arData + idx;
3218 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
3219 				if (idx != k) {
3220 					Bucket *q = Z_ARRVAL_P(stack)->arData + k;
3221 					q->h = k;
3222 					q->key = NULL;
3223 					ZVAL_COPY_VALUE(&q->val, &p->val);
3224 					ZVAL_UNDEF(&p->val);
3225 				}
3226 				k++;
3227 			}
3228 		} else {
3229 			uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3230 
3231 			for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3232 				p = Z_ARRVAL_P(stack)->arData + idx;
3233 				if (Z_TYPE(p->val) == IS_UNDEF) continue;
3234 				if (idx != k) {
3235 					Bucket *q = Z_ARRVAL_P(stack)->arData + k;
3236 					q->h = k;
3237 					q->key = NULL;
3238 					ZVAL_COPY_VALUE(&q->val, &p->val);
3239 					ZVAL_UNDEF(&p->val);
3240 					if (idx == iter_pos) {
3241 						zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3242 						iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3243 					}
3244 				}
3245 				k++;
3246 			}
3247 		}
3248 		Z_ARRVAL_P(stack)->nNumUsed = k;
3249 		Z_ARRVAL_P(stack)->nNextFreeElement = k;
3250 	} else {
3251 		uint32_t k = 0;
3252 		int should_rehash = 0;
3253 
3254 		for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3255 			p = Z_ARRVAL_P(stack)->arData + idx;
3256 			if (Z_TYPE(p->val) == IS_UNDEF) continue;
3257 			if (p->key == NULL) {
3258 				if (p->h != k) {
3259 					p->h = k++;
3260 					should_rehash = 1;
3261 				} else {
3262 					k++;
3263 				}
3264 			}
3265 		}
3266 		Z_ARRVAL_P(stack)->nNextFreeElement = k;
3267 		if (should_rehash) {
3268 			zend_hash_rehash(Z_ARRVAL_P(stack));
3269 		}
3270 	}
3271 
3272 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3273 }
3274 /* }}} */
3275 
3276 /* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
3277    Pushes elements onto the beginning of the array */
3278 PHP_FUNCTION(array_unshift)
3279 {
3280 	zval   *args,			/* Function arguments array */
3281 		   *stack;			/* Input stack */
3282 	HashTable new_hash;		/* New hashtable for the stack */
3283 	int argc;				/* Number of function arguments */
3284 	int i;
3285 	zend_string *key;
3286 	zval *value;
3287 
3288 	ZEND_PARSE_PARAMETERS_START(1, -1)
3289 		Z_PARAM_ARRAY_EX(stack, 0, 1)
3290 		Z_PARAM_VARIADIC('+', args, argc)
3291 	ZEND_PARSE_PARAMETERS_END();
3292 
3293 	zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3294 	for (i = 0; i < argc; i++) {
3295 		Z_TRY_ADDREF(args[i]);
3296 		zend_hash_next_index_insert_new(&new_hash, &args[i]);
3297 	}
3298 
3299 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3300 		if (key) {
3301 			zend_hash_add_new(&new_hash, key, value);
3302 		} else {
3303&n