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