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