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