xref: /php-src/ext/standard/array.c (revision 1aaf2b18)
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_array_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
704 	} else {
705 		zend_array_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_array_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_array_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_array_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_array_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_array_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 
php_array_iter_seek_current(HashTable * array,bool forward_direction)1027 static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
1028 {
1029 	zval *entry;
1030 
1031 	while (true) {
1032 		if ((entry = zend_hash_get_current_data(array)) == NULL) {
1033 			return NULL;
1034 		}
1035 
1036 		ZVAL_DEINDIRECT(entry);
1037 
1038 		/* Possible with an uninitialized typed property */
1039 		if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
1040 			zend_result result;
1041 			if (forward_direction) {
1042 				result = zend_hash_move_forward(array);
1043 			} else {
1044 				result = zend_hash_move_backwards(array);
1045 			}
1046 			if (result != SUCCESS) {
1047 				return NULL;
1048 			}
1049 		} else {
1050 			break;
1051 		}
1052 	}
1053 
1054 	return entry;
1055 }
1056 
php_array_iter_return_current(zval * return_value,HashTable * array,bool forward_direction)1057 static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
1058 {
1059 	zval *entry = php_array_iter_seek_current(array, forward_direction);
1060 	if (EXPECTED(entry)) {
1061 		RETURN_COPY_DEREF(entry);
1062 	} else {
1063 		RETURN_FALSE;
1064 	}
1065 }
1066 
1067 /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1068 PHP_FUNCTION(end)
1069 {
1070 	zval *array_zv;
1071 
1072 	ZEND_PARSE_PARAMETERS_START(1, 1)
1073 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1074 	ZEND_PARSE_PARAMETERS_END();
1075 
1076 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1077 	if (zend_hash_num_elements(array) == 0) {
1078 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1079 		RETURN_FALSE;
1080 	}
1081 	zend_hash_internal_pointer_end(array);
1082 
1083 	if (USED_RET()) {
1084 		php_array_iter_return_current(return_value, array, false);
1085 	}
1086 }
1087 /* }}} */
1088 
1089 /* {{{ Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1090 PHP_FUNCTION(prev)
1091 {
1092 	zval *array_zv;
1093 
1094 	ZEND_PARSE_PARAMETERS_START(1, 1)
1095 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1096 	ZEND_PARSE_PARAMETERS_END();
1097 
1098 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1099 	if (zend_hash_num_elements(array) == 0) {
1100 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1101 		RETURN_FALSE;
1102 	}
1103 	zend_hash_move_backwards(array);
1104 
1105 	if (USED_RET()) {
1106 		php_array_iter_return_current(return_value, array, false);
1107 	}
1108 }
1109 /* }}} */
1110 
1111 /* {{{ Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1112 PHP_FUNCTION(next)
1113 {
1114 	zval *array_zv;
1115 
1116 	ZEND_PARSE_PARAMETERS_START(1, 1)
1117 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1118 	ZEND_PARSE_PARAMETERS_END();
1119 
1120 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1121 	if (zend_hash_num_elements(array) == 0) {
1122 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1123 		RETURN_FALSE;
1124 	}
1125 	zend_hash_move_forward(array);
1126 
1127 	if (USED_RET()) {
1128 		php_array_iter_return_current(return_value, array, true);
1129 	}
1130 }
1131 /* }}} */
1132 
1133 /* {{{ Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1134 PHP_FUNCTION(reset)
1135 {
1136 	zval *array_zv;
1137 
1138 	ZEND_PARSE_PARAMETERS_START(1, 1)
1139 		Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1140 	ZEND_PARSE_PARAMETERS_END();
1141 
1142 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1143 	if (zend_hash_num_elements(array) == 0) {
1144 		/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1145 		RETURN_FALSE;
1146 	}
1147 	zend_hash_internal_pointer_reset(array);
1148 
1149 	if (USED_RET()) {
1150 		php_array_iter_return_current(return_value, array, true);
1151 	}
1152 }
1153 /* }}} */
1154 
1155 /* {{{ Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1156 PHP_FUNCTION(current)
1157 {
1158 	zval *array_zv;
1159 
1160 	ZEND_PARSE_PARAMETERS_START(1, 1)
1161 		Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1162 	ZEND_PARSE_PARAMETERS_END();
1163 
1164 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1165 	php_array_iter_return_current(return_value, array, true);
1166 }
1167 /* }}} */
1168 
1169 /* {{{ Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1170 PHP_FUNCTION(key)
1171 {
1172 	zval *array_zv;
1173 
1174 	ZEND_PARSE_PARAMETERS_START(1, 1)
1175 		Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1176 	ZEND_PARSE_PARAMETERS_END();
1177 
1178 	HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1179 	zval *entry = php_array_iter_seek_current(array, true);
1180 	if (EXPECTED(entry)) {
1181 		zend_hash_get_current_key_zval(array, return_value);
1182 	}
1183 }
1184 /* }}} */
1185 
php_data_compare(const void * f,const void * s)1186 static int php_data_compare(const void *f, const void *s) /* {{{ */
1187 {
1188 	return zend_compare((zval*)f, (zval*)s);
1189 }
1190 /* }}} */
1191 
1192 /* {{{
1193  * proto mixed min(array values)
1194  * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1195    Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1196 PHP_FUNCTION(min)
1197 {
1198 	uint32_t argc;
1199 	zval *args = NULL;
1200 
1201 	ZEND_PARSE_PARAMETERS_START(1, -1)
1202 		Z_PARAM_VARIADIC('+', args, argc)
1203 	ZEND_PARSE_PARAMETERS_END();
1204 
1205 	/* mixed min ( array $values ) */
1206 	if (argc == 1) {
1207 		if (Z_TYPE(args[0]) != IS_ARRAY) {
1208 			zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1209 			RETURN_THROWS();
1210 		} else {
1211 			zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0);
1212 			if (result) {
1213 				RETURN_COPY_DEREF(result);
1214 			} else {
1215 				zend_argument_value_error(1, "must contain at least one element");
1216 				RETURN_THROWS();
1217 			}
1218 		}
1219 	} else {
1220 		/* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1221 		zval *min;
1222 		uint32_t i;
1223 
1224 		min = &args[0];
1225 		zend_long min_lval;
1226 		double min_dval;
1227 
1228 		if (Z_TYPE_P(min) == IS_LONG) {
1229 			min_lval = Z_LVAL_P(min);
1230 
1231 			for (i = 1; i < argc; i++) {
1232 				if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1233 					if (min_lval > Z_LVAL(args[i])) {
1234 						min_lval = Z_LVAL(args[i]);
1235 						min = &args[i];
1236 					}
1237 				} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) {
1238 					/* if min_lval can be exactly represented as a double, go to double dedicated code */
1239 					min_dval = (double) min_lval;
1240 					goto double_compare;
1241 				} else {
1242 					goto generic_compare;
1243 				}
1244 			}
1245 
1246 			RETURN_LONG(min_lval);
1247 		} else if (Z_TYPE_P(min) == IS_DOUBLE) {
1248 			min_dval = Z_DVAL_P(min);
1249 
1250 			for (i = 1; i < argc; i++) {
1251 				if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1252 					double_compare:
1253 					if (min_dval > Z_DVAL(args[i])) {
1254 						min_dval = Z_DVAL(args[i]);
1255 						min = &args[i];
1256 					}
1257 				} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1258 					/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1259 					if (min_dval > (double)Z_LVAL(args[i])) {
1260 						min_dval = (double)Z_LVAL(args[i]);
1261 						min = &args[i];
1262 					}
1263 				} else {
1264 					goto generic_compare;
1265 				}
1266 			}
1267 		} else {
1268 			for (i = 1; i < argc; i++) {
1269 				generic_compare:
1270 				if (zend_compare(&args[i], min) < 0) {
1271 					min = &args[i];
1272 				}
1273 			}
1274 		}
1275 
1276 		RETURN_COPY(min);
1277 	}
1278 }
1279 /* }}} */
1280 
1281 ZEND_FRAMELESS_FUNCTION(min, 2)
1282 {
1283 	zval *lhs, *rhs;
1284 
1285 	Z_FLF_PARAM_ZVAL(1, lhs);
1286 	Z_FLF_PARAM_ZVAL(2, rhs);
1287 
1288 	double lhs_dval;
1289 
1290 	if (Z_TYPE_P(lhs) == IS_LONG) {
1291 		zend_long lhs_lval = Z_LVAL_P(lhs);
1292 
1293 		if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
1294 			RETURN_COPY_VALUE(lhs_lval < Z_LVAL_P(rhs) ? lhs : rhs);
1295 		} else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) {
1296 			/* if lhs_lval can be exactly represented as a double, go to double dedicated code */
1297 			lhs_dval = (double) lhs_lval;
1298 			goto double_compare;
1299 		} else {
1300 			goto generic_compare;
1301 		}
1302 	} else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
1303 		lhs_dval = Z_DVAL_P(lhs);
1304 
1305 		if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
1306 double_compare:
1307 			RETURN_COPY_VALUE(lhs_dval < Z_DVAL_P(rhs) ? lhs : rhs);
1308 		} else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
1309 			/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1310 			RETURN_COPY_VALUE(lhs_dval < (double)Z_LVAL_P(rhs) ? lhs : rhs);
1311 		} else {
1312 			goto generic_compare;
1313 		}
1314 	} else {
1315 generic_compare:
1316 		RETURN_COPY(zend_compare(lhs, rhs) < 0 ? lhs : rhs);
1317 	}
1318 }
1319 
1320 /* {{{
1321  * proto mixed max(array values)
1322  * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1323    Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)1324 PHP_FUNCTION(max)
1325 {
1326 	zval *args = NULL;
1327 	uint32_t argc;
1328 
1329 	ZEND_PARSE_PARAMETERS_START(1, -1)
1330 		Z_PARAM_VARIADIC('+', args, argc)
1331 	ZEND_PARSE_PARAMETERS_END();
1332 
1333 	/* mixed max ( array $values ) */
1334 	if (argc == 1) {
1335 		if (Z_TYPE(args[0]) != IS_ARRAY) {
1336 			zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
1337 			RETURN_THROWS();
1338 		} else {
1339 			zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1);
1340 			if (result) {
1341 				RETURN_COPY_DEREF(result);
1342 			} else {
1343 				zend_argument_value_error(1, "must contain at least one element");
1344 				RETURN_THROWS();
1345 			}
1346 		}
1347 	} else {
1348 		/* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1349 		zval *max;
1350 		uint32_t i;
1351 
1352 		max = &args[0];
1353 		zend_long max_lval;
1354 		double max_dval;
1355 
1356 		if (Z_TYPE_P(max) == IS_LONG) {
1357 			max_lval = Z_LVAL_P(max);
1358 
1359 			for (i = 1; i < argc; i++) {
1360 				if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1361 					if (max_lval < Z_LVAL(args[i])) {
1362 						max_lval = Z_LVAL(args[i]);
1363 						max = &args[i];
1364 					}
1365 				} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) {
1366 					/* if max_lval can be exactly represented as a double, go to double dedicated code */
1367 					max_dval = (double) max_lval;
1368 					goto double_compare;
1369 				} else {
1370 					goto generic_compare;
1371 				}
1372 			}
1373 
1374 			RETURN_LONG(max_lval);
1375 		} else if (Z_TYPE_P(max) == IS_DOUBLE) {
1376 			max_dval = Z_DVAL_P(max);
1377 
1378 			for (i = 1; i < argc; i++) {
1379 				if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1380 					double_compare:
1381 					if (max_dval < Z_DVAL(args[i])) {
1382 						max_dval = Z_DVAL(args[i]);
1383 						max = &args[i];
1384 					}
1385 				} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1386 					/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1387 					if (max_dval < (double)Z_LVAL(args[i])) {
1388 						max_dval = (double)Z_LVAL(args[i]);
1389 						max = &args[i];
1390 					}
1391 				} else {
1392 					goto generic_compare;
1393 				}
1394 			}
1395 		} else {
1396 			for (i = 1; i < argc; i++) {
1397 				generic_compare:
1398 				if (zend_compare(&args[i], max) > 0) {
1399 					max = &args[i];
1400 				}
1401 			}
1402 		}
1403 
1404 		RETURN_COPY(max);
1405 	}
1406 }
1407 /* }}} */
1408 
1409 ZEND_FRAMELESS_FUNCTION(max, 2)
1410 {
1411 	zval *lhs, *rhs;
1412 
1413 	Z_FLF_PARAM_ZVAL(1, lhs);
1414 	Z_FLF_PARAM_ZVAL(2, rhs);
1415 
1416 	double lhs_dval;
1417 
1418 	if (Z_TYPE_P(lhs) == IS_LONG) {
1419 		zend_long lhs_lval = Z_LVAL_P(lhs);
1420 
1421 		if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
1422 			RETURN_COPY_VALUE(lhs_lval >= Z_LVAL_P(rhs) ? lhs : rhs);
1423 		} else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) {
1424 			/* if lhs_lval can be exactly represented as a double, go to double dedicated code */
1425 			lhs_dval = (double) lhs_lval;
1426 			goto double_compare;
1427 		} else {
1428 			goto generic_compare;
1429 		}
1430 	} else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
1431 		lhs_dval = Z_DVAL_P(lhs);
1432 
1433 		if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
1434 double_compare:
1435 			RETURN_COPY_VALUE(lhs_dval >= Z_DVAL_P(rhs) ? lhs : rhs);
1436 		} else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
1437 			/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1438 			RETURN_COPY_VALUE(lhs_dval >= (double)Z_LVAL_P(rhs) ? lhs : rhs);
1439 		} else {
1440 			goto generic_compare;
1441 		}
1442 	} else {
1443 generic_compare:
1444 		RETURN_COPY(zend_compare(lhs, rhs) >= 0 ? lhs : rhs);
1445 	}
1446 }
1447 
1448 typedef struct {
1449 	zend_fcall_info fci;
1450 	zend_fcall_info_cache fci_cache;
1451 } php_array_walk_context;
1452 
php_array_walk(php_array_walk_context * context,zval * array,zval * userdata,bool recursive)1453 static zend_result php_array_walk(
1454 	php_array_walk_context *context, zval *array, zval *userdata, bool recursive)
1455 {
1456 	zval args[3],		/* Arguments to userland function */
1457 		 retval,		/* Return value - unused */
1458 		 *zv;
1459 	HashTable *target_hash = HASH_OF(array);
1460 	HashPosition pos;
1461 	uint32_t ht_iter;
1462 	zend_result result = SUCCESS;
1463 
1464 	/* Create a local copy of fci, as we want to use different arguments at different
1465 	 * levels of recursion. */
1466 	zend_fcall_info fci = context->fci;
1467 
1468 	if (zend_hash_num_elements(target_hash) == 0) {
1469 		return result;
1470 	}
1471 
1472 	/* Set up known arguments */
1473 	ZVAL_UNDEF(&args[1]);
1474 	if (userdata) {
1475 		ZVAL_COPY(&args[2], userdata);
1476 	}
1477 
1478 	fci.retval = &retval;
1479 	fci.param_count = userdata ? 3 : 2;
1480 	fci.params = args;
1481 
1482 	zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1483 	ht_iter = zend_hash_iterator_add(target_hash, pos);
1484 
1485 	/* Iterate through hash */
1486 	do {
1487 		/* Retrieve value */
1488 		zv = zend_hash_get_current_data_ex(target_hash, &pos);
1489 		if (zv == NULL) {
1490 			break;
1491 		}
1492 
1493 		/* Skip undefined indirect elements */
1494 		if (Z_TYPE_P(zv) == IS_INDIRECT) {
1495 			zv = Z_INDIRECT_P(zv);
1496 			if (Z_TYPE_P(zv) == IS_UNDEF) {
1497 				zend_hash_move_forward_ex(target_hash, &pos);
1498 				continue;
1499 			}
1500 
1501 			/* Add type source for property references. */
1502 			if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1503 				zend_property_info *prop_info =
1504 					zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1505 				if (prop_info) {
1506 					ZVAL_NEW_REF(zv, zv);
1507 					ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1508 				}
1509 			}
1510 		}
1511 
1512 		/* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1513 		ZVAL_MAKE_REF(zv);
1514 
1515 		/* Retrieve key */
1516 		zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1517 
1518 		/* Move to next element already now -- this mirrors the approach used by foreach
1519 		 * and ensures proper behavior with regard to modifications. */
1520 		zend_hash_move_forward_ex(target_hash, &pos);
1521 
1522 		/* Back up hash position, as it may change */
1523 		EG(ht_iterators)[ht_iter].pos = pos;
1524 
1525 		if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1526 			HashTable *thash;
1527 			zval ref;
1528 			ZVAL_COPY_VALUE(&ref, zv);
1529 
1530 			ZVAL_DEREF(zv);
1531 			SEPARATE_ARRAY(zv);
1532 			thash = Z_ARRVAL_P(zv);
1533 			if (GC_IS_RECURSIVE(thash)) {
1534 				zend_throw_error(NULL, "Recursion detected");
1535 				result = FAILURE;
1536 				break;
1537 			}
1538 
1539 			Z_ADDREF(ref);
1540 			GC_PROTECT_RECURSION(thash);
1541 			result = php_array_walk(context, zv, userdata, recursive);
1542 			if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1543 				/* If the hashtable changed in the meantime, we'll "leak" this apply count
1544 				 * increment -- our reference to thash is no longer valid. */
1545 				GC_UNPROTECT_RECURSION(thash);
1546 			}
1547 			zval_ptr_dtor(&ref);
1548 		} else {
1549 			ZVAL_COPY(&args[0], zv);
1550 
1551 			/* Call the userland function */
1552 			result = zend_call_function(&fci, &context->fci_cache);
1553 			if (result == SUCCESS) {
1554 				zval_ptr_dtor(&retval);
1555 			}
1556 
1557 			zval_ptr_dtor(&args[0]);
1558 		}
1559 
1560 		if (Z_TYPE(args[1]) != IS_UNDEF) {
1561 			zval_ptr_dtor(&args[1]);
1562 			ZVAL_UNDEF(&args[1]);
1563 		}
1564 
1565 		if (result == FAILURE) {
1566 			break;
1567 		}
1568 
1569 		/* Reload array and position -- both may have changed */
1570 		if (Z_TYPE_P(array) == IS_ARRAY) {
1571 			pos = zend_hash_iterator_pos_ex(ht_iter, array);
1572 			target_hash = Z_ARRVAL_P(array);
1573 		} else if (Z_TYPE_P(array) == IS_OBJECT) {
1574 			target_hash = Z_OBJPROP_P(array);
1575 			pos = zend_hash_iterator_pos(ht_iter, target_hash);
1576 		} else {
1577 			zend_type_error("Iterated value is no longer an array or object");
1578 			result = FAILURE;
1579 			break;
1580 		}
1581 	} while (!EG(exception));
1582 
1583 	if (userdata) {
1584 		zval_ptr_dtor(&args[2]);
1585 	}
1586 	zend_hash_iterator_del(ht_iter);
1587 	return result;
1588 }
1589 
1590 /* {{{ Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1591 PHP_FUNCTION(array_walk)
1592 {
1593 	zval *array;
1594 	zval *userdata = NULL;
1595 	php_array_walk_context context;
1596 
1597 	ZEND_PARSE_PARAMETERS_START(2, 3)
1598 		Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1599 		Z_PARAM_FUNC(context.fci, context.fci_cache)
1600 		Z_PARAM_OPTIONAL
1601 		Z_PARAM_ZVAL(userdata)
1602 	ZEND_PARSE_PARAMETERS_END();
1603 
1604 	php_array_walk(&context, array, userdata, /* recursive */ false);
1605 	RETURN_TRUE;
1606 }
1607 /* }}} */
1608 
1609 /* {{{ Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1610 PHP_FUNCTION(array_walk_recursive)
1611 {
1612 	zval *array;
1613 	zval *userdata = NULL;
1614 	php_array_walk_context context;
1615 
1616 	ZEND_PARSE_PARAMETERS_START(2, 3)
1617 		Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1618 		Z_PARAM_FUNC(context.fci, context.fci_cache)
1619 		Z_PARAM_OPTIONAL
1620 		Z_PARAM_ZVAL(userdata)
1621 	ZEND_PARSE_PARAMETERS_END();
1622 
1623 	php_array_walk(&context, array, userdata, /* recursive */ true);
1624 	RETURN_TRUE;
1625 }
1626 /* }}} */
1627 
1628 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1629  * 0 = return boolean
1630  * 1 = return key
1631  */
_php_search_array(zval * return_value,zval * value,zval * array,bool strict,int behavior)1632 static inline void _php_search_array(zval *return_value, zval *value, zval *array, bool strict, int behavior) /* {{{ */
1633 {
1634 	zval *entry; /* pointer to array entry */
1635 	zend_ulong num_idx;
1636 	zend_string *str_idx;
1637 
1638 	if (strict) {
1639 		if (Z_TYPE_P(value) == IS_LONG) {
1640 			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1641 				ZVAL_DEREF(entry);
1642 				if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1643 					if (behavior == 0) {
1644 						RETURN_TRUE;
1645 					} else {
1646 						if (str_idx) {
1647 							RETURN_STR_COPY(str_idx);
1648 						} else {
1649 							RETURN_LONG(num_idx);
1650 						}
1651 					}
1652 				}
1653 			} ZEND_HASH_FOREACH_END();
1654 		} else {
1655 			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1656 				ZVAL_DEREF(entry);
1657 				if (fast_is_identical_function(value, entry)) {
1658 					if (behavior == 0) {
1659 						RETURN_TRUE;
1660 					} else {
1661 						if (str_idx) {
1662 							RETURN_STR_COPY(str_idx);
1663 						} else {
1664 							RETURN_LONG(num_idx);
1665 						}
1666 					}
1667 				}
1668 			} ZEND_HASH_FOREACH_END();
1669 		}
1670 	} else {
1671 		if (Z_TYPE_P(value) == IS_LONG) {
1672 			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1673 				if (fast_equal_check_long(value, entry)) {
1674 					if (behavior == 0) {
1675 						RETURN_TRUE;
1676 					} else {
1677 						if (str_idx) {
1678 							RETURN_STR_COPY(str_idx);
1679 						} else {
1680 							RETURN_LONG(num_idx);
1681 						}
1682 					}
1683 				}
1684 			} ZEND_HASH_FOREACH_END();
1685 		} else if (Z_TYPE_P(value) == IS_STRING) {
1686 			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1687 				if (fast_equal_check_string(value, entry)) {
1688 					if (behavior == 0) {
1689 						RETURN_TRUE;
1690 					} else {
1691 						if (str_idx) {
1692 							RETURN_STR_COPY(str_idx);
1693 						} else {
1694 							RETURN_LONG(num_idx);
1695 						}
1696 					}
1697 				}
1698 			} ZEND_HASH_FOREACH_END();
1699 		} else {
1700 			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1701 				if (fast_equal_check_function(value, entry)) {
1702 					if (behavior == 0) {
1703 						RETURN_TRUE;
1704 					} else {
1705 						if (str_idx) {
1706 							RETURN_STR_COPY(str_idx);
1707 						} else {
1708 							RETURN_LONG(num_idx);
1709 						}
1710 					}
1711 				}
1712 			} ZEND_HASH_FOREACH_END();
1713 		}
1714 	}
1715 
1716 	RETURN_FALSE;
1717 }
1718 /* }}} */
1719 
1720 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1721  * 0 = return boolean
1722  * 1 = return key
1723  */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1724 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1725 {
1726 	zval *value,		/* value to check for */
1727 		 *array;		/* array to check in */
1728 	bool strict = 0;	/* strict comparison or not */
1729 
1730 	ZEND_PARSE_PARAMETERS_START(2, 3)
1731 		Z_PARAM_ZVAL(value)
1732 		Z_PARAM_ARRAY(array)
1733 		Z_PARAM_OPTIONAL
1734 		Z_PARAM_BOOL(strict)
1735 	ZEND_PARSE_PARAMETERS_END();
1736 
1737 	_php_search_array(return_value, value, array, strict, behavior);
1738 }
1739 
1740 /* {{{ Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1741 PHP_FUNCTION(in_array)
1742 {
1743 	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1744 }
1745 /* }}} */
1746 
1747 ZEND_FRAMELESS_FUNCTION(in_array, 2)
1748 {
1749 	zval *value, *array;
1750 
1751 	Z_FLF_PARAM_ZVAL(1, value);
1752 	Z_FLF_PARAM_ARRAY(2, array);
1753 
1754 	_php_search_array(return_value, value, array, false, 0);
1755 
1756 flf_clean:;
1757 }
1758 
1759 ZEND_FRAMELESS_FUNCTION(in_array, 3)
1760 {
1761 	zval *value, *array;
1762 	bool strict;
1763 
1764 	Z_FLF_PARAM_ZVAL(1, value);
1765 	Z_FLF_PARAM_ARRAY(2, array);
1766 	Z_FLF_PARAM_BOOL(3, strict);
1767 
1768 	_php_search_array(return_value, value, array, strict, 0);
1769 
1770 flf_clean:;
1771 }
1772 
1773 /* {{{ Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1774 PHP_FUNCTION(array_search)
1775 {
1776 	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1777 }
1778 /* }}} */
1779 
php_valid_var_name(const char * var_name,size_t var_name_len)1780 static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
1781 {
1782 #if 1
1783 	/* first 256 bits for first character, and second 256 bits for the next */
1784 	static const uint32_t charset[8] = {
1785 	     /*  31      0   63     32   95     64   127    96 */
1786 			0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1787 			0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1788 	static const uint32_t charset2[8] = {
1789 	     /*  31      0   63     32   95     64   127    96 */
1790 			0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1791 			0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1792 #endif
1793 	size_t i;
1794 	uint32_t ch;
1795 
1796 	if (UNEXPECTED(!var_name_len)) {
1797 		return 0;
1798 	}
1799 
1800 	/* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1801 	ch = (uint32_t)((unsigned char *)var_name)[0];
1802 #if 1
1803 	if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1804 #else
1805 	if (var_name[0] != '_' &&
1806 		(ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1807 		(ch < 97  /* a    */ || /* z    */ ch > 122) &&
1808 		(ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1809 	) {
1810 #endif
1811 		return 0;
1812 	}
1813 
1814 	/* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1815 	if (var_name_len > 1) {
1816 		i = 1;
1817 		do {
1818 			ch = (uint32_t)((unsigned char *)var_name)[i];
1819 #if 1
1820 			if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1821 #else
1822 			if (var_name[i] != '_' &&
1823 				(ch < 48  /* 0    */ || /* 9    */ ch > 57)  &&
1824 				(ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1825 				(ch < 97  /* a    */ || /* z    */ ch > 122) &&
1826 				(ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1827 			) {
1828 #endif
1829 				return 0;
1830 			}
1831 		} while (++i < var_name_len);
1832 	}
1833 	return 1;
1834 }
1835 /* }}} */
1836 
1837 PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore) /* {{{ */
1838 {
1839 	ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1840 	memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
1841 
1842 	if (add_underscore) {
1843 		Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
1844 	}
1845 
1846 	memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1847 
1848 	return SUCCESS;
1849 }
1850 /* }}} */
1851 
1852 static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1853 {
1854 	zend_long count = 0;
1855 	zend_string *var_name;
1856 	zval *entry, *orig_var;
1857 
1858 	if (HT_IS_PACKED(arr)) {
1859 		return 0;
1860 	}
1861 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1862 		if (!var_name) {
1863 			continue;
1864 		}
1865 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1866 		if (orig_var) {
1867 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1868 				orig_var = Z_INDIRECT_P(orig_var);
1869 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1870 					continue;
1871 				}
1872 			}
1873 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1874 				continue;
1875 			}
1876 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1877 				continue;
1878 			}
1879 			if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1880 				zend_throw_error(NULL, "Cannot re-assign $this");
1881 				return -1;
1882 			}
1883 			if (Z_ISREF_P(entry)) {
1884 				Z_ADDREF_P(entry);
1885 			} else {
1886 				ZVAL_MAKE_REF_EX(entry, 2);
1887 			}
1888 			zval_ptr_dtor(orig_var);
1889 			ZVAL_REF(orig_var, Z_REF_P(entry));
1890 			count++;
1891 		}
1892 	} ZEND_HASH_FOREACH_END();
1893 
1894 	return count;
1895 }
1896 /* }}} */
1897 
1898 static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1899 {
1900 	zend_long count = 0;
1901 	zend_string *var_name;
1902 	zval *entry, *orig_var;
1903 
1904 	if (HT_IS_PACKED(arr)) {
1905 		return 0;
1906 	}
1907 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1908 		if (!var_name) {
1909 			continue;
1910 		}
1911 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1912 		if (orig_var) {
1913 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1914 				orig_var = Z_INDIRECT_P(orig_var);
1915 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1916 					continue;
1917 				}
1918 			}
1919 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1920 				continue;
1921 			}
1922 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1923 				continue;
1924 			}
1925 			if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1926 				zend_throw_error(NULL, "Cannot re-assign $this");
1927 				return -1;
1928 			}
1929 			ZVAL_DEREF(entry);
1930 			ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1931 			if (UNEXPECTED(EG(exception))) {
1932 				return -1;
1933 			}
1934 			count++;
1935 		}
1936 	} ZEND_HASH_FOREACH_END();
1937 
1938 	return count;
1939 }
1940 /* }}} */
1941 
1942 static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1943 {
1944 	zend_long count = 0;
1945 	zend_string *var_name;
1946 	zval *entry, *orig_var;
1947 
1948 	if (HT_IS_PACKED(arr)) {
1949 		return 0;
1950 	}
1951 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1952 		if (!var_name) {
1953 			continue;
1954 		}
1955 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1956 			continue;
1957 		}
1958 		if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1959 			zend_throw_error(NULL, "Cannot re-assign $this");
1960 			return -1;
1961 		}
1962 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1963 		if (orig_var) {
1964 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1965 				orig_var = Z_INDIRECT_P(orig_var);
1966 			}
1967 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
1968 				continue;
1969 			}
1970 			if (Z_ISREF_P(entry)) {
1971 				Z_ADDREF_P(entry);
1972 			} else {
1973 				ZVAL_MAKE_REF_EX(entry, 2);
1974 			}
1975 			zval_ptr_dtor(orig_var);
1976 			ZVAL_REF(orig_var, Z_REF_P(entry));
1977 		} else {
1978 			if (Z_ISREF_P(entry)) {
1979 				Z_ADDREF_P(entry);
1980 			} else {
1981 				ZVAL_MAKE_REF_EX(entry, 2);
1982 			}
1983 			zend_hash_add_new(symbol_table, var_name, entry);
1984 		}
1985 		count++;
1986 	} ZEND_HASH_FOREACH_END();
1987 
1988 	return count;
1989 }
1990 /* }}} */
1991 
1992 static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1993 {
1994 	zend_long count = 0;
1995 	zend_string *var_name;
1996 	zval *entry, *orig_var;
1997 
1998 	if (HT_IS_PACKED(arr)) {
1999 		return 0;
2000 	}
2001 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2002 		if (!var_name) {
2003 			continue;
2004 		}
2005 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2006 			continue;
2007 		}
2008 		if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2009 			zend_throw_error(NULL, "Cannot re-assign $this");
2010 			return -1;
2011 		}
2012 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2013 		if (orig_var) {
2014 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2015 				orig_var = Z_INDIRECT_P(orig_var);
2016 			}
2017 			if (zend_string_equals_literal(var_name, "GLOBALS")) {
2018 				continue;
2019 			}
2020 			ZVAL_DEREF(entry);
2021 			ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2022 			if (UNEXPECTED(EG(exception))) {
2023 				return -1;
2024 			}
2025 		} else {
2026 			ZVAL_DEREF(entry);
2027 			Z_TRY_ADDREF_P(entry);
2028 			zend_hash_add_new(symbol_table, var_name, entry);
2029 		}
2030 		count++;
2031 	} ZEND_HASH_FOREACH_END();
2032 
2033 	return count;
2034 }
2035 /* }}} */
2036 
2037 static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2038 {
2039 	zend_long count = 0;
2040 	zend_string *var_name;
2041 	zval *entry, *orig_var, final_name;
2042 
2043 	if (HT_IS_PACKED(arr)) {
2044 		return 0;
2045 	}
2046 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2047 		if (!var_name) {
2048 			continue;
2049 		}
2050 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2051 		if (orig_var) {
2052 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2053 				orig_var = Z_INDIRECT_P(orig_var);
2054 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2055 					if (Z_ISREF_P(entry)) {
2056 						Z_ADDREF_P(entry);
2057 					} else {
2058 						ZVAL_MAKE_REF_EX(entry, 2);
2059 					}
2060 					ZVAL_REF(orig_var, Z_REF_P(entry));
2061 					count++;
2062 					continue;
2063 				}
2064 			}
2065 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2066 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2067 				if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2068 					zend_throw_error(NULL, "Cannot re-assign $this");
2069 					return -1;
2070 				} else {
2071 					if (Z_ISREF_P(entry)) {
2072 						Z_ADDREF_P(entry);
2073 					} else {
2074 						ZVAL_MAKE_REF_EX(entry, 2);
2075 					}
2076 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2077 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2078 							orig_var = Z_INDIRECT_P(orig_var);
2079 						}
2080 						zval_ptr_dtor(orig_var);
2081 						ZVAL_REF(orig_var, Z_REF_P(entry));
2082 					} else {
2083 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2084 					}
2085 					count++;
2086 				}
2087 			}
2088 			zval_ptr_dtor_str(&final_name);
2089 		}
2090 	} ZEND_HASH_FOREACH_END();
2091 
2092 	return count;
2093 }
2094 /* }}} */
2095 
2096 static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2097 {
2098 	zend_long count = 0;
2099 	zend_string *var_name;
2100 	zval *entry, *orig_var, final_name;
2101 
2102 	if (HT_IS_PACKED(arr)) {
2103 		return 0;
2104 	}
2105 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2106 		if (!var_name) {
2107 			continue;
2108 		}
2109 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2110 		if (orig_var) {
2111 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2112 				orig_var = Z_INDIRECT_P(orig_var);
2113 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2114 					ZVAL_COPY_DEREF(orig_var, entry);
2115 					count++;
2116 					continue;
2117 				}
2118 			}
2119 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2120 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2121 				if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2122 					zend_throw_error(NULL, "Cannot re-assign $this");
2123 					return -1;
2124 				} else {
2125 					ZVAL_DEREF(entry);
2126 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2127 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2128 							orig_var = Z_INDIRECT_P(orig_var);
2129 						}
2130 						ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2131 						if (UNEXPECTED(EG(exception))) {
2132 							zend_string_release_ex(Z_STR(final_name), 0);
2133 							return -1;
2134 						}
2135 					} else {
2136 						Z_TRY_ADDREF_P(entry);
2137 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2138 					}
2139 					count++;
2140 				}
2141 			}
2142 			zval_ptr_dtor_str(&final_name);
2143 		}
2144 	} ZEND_HASH_FOREACH_END();
2145 
2146 	return count;
2147 }
2148 /* }}} */
2149 
2150 static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2151 {
2152 	zend_long count = 0;
2153 	zend_string *var_name;
2154 	zval *entry, *orig_var, final_name;
2155 
2156 	if (HT_IS_PACKED(arr)) {
2157 		return 0;
2158 	}
2159 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2160 		if (!var_name) {
2161 			continue;
2162 		}
2163 		if (ZSTR_LEN(var_name) == 0) {
2164 			continue;
2165 		}
2166 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2167 		if (orig_var) {
2168 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2169 				orig_var = Z_INDIRECT_P(orig_var);
2170 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2171 					if (Z_ISREF_P(entry)) {
2172 						Z_ADDREF_P(entry);
2173 					} else {
2174 						ZVAL_MAKE_REF_EX(entry, 2);
2175 					}
2176 					ZVAL_REF(orig_var, Z_REF_P(entry));
2177 					count++;
2178 					continue;
2179 				}
2180 			}
2181 prefix:
2182 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2183 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2184 				if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2185 					zend_throw_error(NULL, "Cannot re-assign $this");
2186 					return -1;
2187 				} else {
2188 					if (Z_ISREF_P(entry)) {
2189 						Z_ADDREF_P(entry);
2190 					} else {
2191 						ZVAL_MAKE_REF_EX(entry, 2);
2192 					}
2193 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2194 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2195 							orig_var = Z_INDIRECT_P(orig_var);
2196 						}
2197 						zval_ptr_dtor(orig_var);
2198 						ZVAL_REF(orig_var, Z_REF_P(entry));
2199 					} else {
2200 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2201 					}
2202 					count++;
2203 				}
2204 			}
2205 			zval_ptr_dtor_str(&final_name);
2206 		} else {
2207 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2208 				continue;
2209 			}
2210 			if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2211 				goto prefix;
2212 			}
2213 			if (Z_ISREF_P(entry)) {
2214 				Z_ADDREF_P(entry);
2215 			} else {
2216 				ZVAL_MAKE_REF_EX(entry, 2);
2217 			}
2218 			zend_hash_add_new(symbol_table, var_name, entry);
2219 			count++;
2220 		}
2221 	} ZEND_HASH_FOREACH_END();
2222 
2223 	return count;
2224 }
2225 /* }}} */
2226 
2227 static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2228 {
2229 	zend_long count = 0;
2230 	zend_string *var_name;
2231 	zval *entry, *orig_var, final_name;
2232 
2233 	if (HT_IS_PACKED(arr)) {
2234 		return 0;
2235 	}
2236 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2237 		if (!var_name) {
2238 			continue;
2239 		}
2240 		if (ZSTR_LEN(var_name) == 0) {
2241 			continue;
2242 		}
2243 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2244 		if (orig_var) {
2245 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2246 				orig_var = Z_INDIRECT_P(orig_var);
2247 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2248 					ZVAL_COPY_DEREF(orig_var, entry);
2249 					count++;
2250 					continue;
2251 				}
2252 			}
2253 prefix:
2254 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2255 			if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2256 				if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2257 					zend_throw_error(NULL, "Cannot re-assign $this");
2258 					return -1;
2259 				} else {
2260 					ZVAL_DEREF(entry);
2261 					if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2262 						if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2263 							orig_var = Z_INDIRECT_P(orig_var);
2264 						}
2265 						ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2266 						if (UNEXPECTED(EG(exception))) {
2267 							zend_string_release_ex(Z_STR(final_name), 0);
2268 							return -1;
2269 						}
2270 					} else {
2271 						Z_TRY_ADDREF_P(entry);
2272 						zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2273 					}
2274 					count++;
2275 				}
2276 			}
2277 			zval_ptr_dtor_str(&final_name);
2278 		} else {
2279 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2280 				continue;
2281 			}
2282 			if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2283 				goto prefix;
2284 			}
2285 			ZVAL_DEREF(entry);
2286 			Z_TRY_ADDREF_P(entry);
2287 			zend_hash_add_new(symbol_table, var_name, entry);
2288 			count++;
2289 		}
2290 	} ZEND_HASH_FOREACH_END();
2291 
2292 	return count;
2293 }
2294 /* }}} */
2295 
2296 static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2297 {
2298 	zend_long count = 0;
2299 	zend_string *var_name;
2300 	zend_ulong num_key;
2301 	zval *entry, *orig_var, final_name;
2302 
2303 	ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2304 		if (var_name) {
2305 			if (ZSTR_LEN(var_name) == 0) {
2306 				continue;
2307 			}
2308 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2309 		} else {
2310 			zend_string *str = zend_long_to_str(num_key);
2311 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2312 			zend_string_release_ex(str, 0);
2313 		}
2314 		if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2315 			if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2316 				zend_throw_error(NULL, "Cannot re-assign $this");
2317 				return -1;
2318 			} else {
2319 				if (Z_ISREF_P(entry)) {
2320 					Z_ADDREF_P(entry);
2321 				} else {
2322 					ZVAL_MAKE_REF_EX(entry, 2);
2323 				}
2324 				if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2325 					if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2326 						orig_var = Z_INDIRECT_P(orig_var);
2327 					}
2328 					zval_ptr_dtor(orig_var);
2329 					ZVAL_REF(orig_var, Z_REF_P(entry));
2330 				} else {
2331 					zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2332 				}
2333 				count++;
2334 			}
2335 		}
2336 		zval_ptr_dtor_str(&final_name);
2337 	} ZEND_HASH_FOREACH_END();
2338 
2339 	return count;
2340 }
2341 /* }}} */
2342 
2343 static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2344 {
2345 	zend_long count = 0;
2346 	zend_string *var_name;
2347 	zend_ulong num_key;
2348 	zval *entry, *orig_var, final_name;
2349 
2350 	ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2351 		if (var_name) {
2352 			if (ZSTR_LEN(var_name) == 0) {
2353 				continue;
2354 			}
2355 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2356 		} else {
2357 			zend_string *str = zend_long_to_str(num_key);
2358 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2359 			zend_string_release_ex(str, 0);
2360 		}
2361 		if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2362 			if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2363 				zend_throw_error(NULL, "Cannot re-assign $this");
2364 				return -1;
2365 			} else {
2366 				ZVAL_DEREF(entry);
2367 				if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2368 					if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2369 						orig_var = Z_INDIRECT_P(orig_var);
2370 					}
2371 					ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2372 					if (UNEXPECTED(EG(exception))) {
2373 						zend_string_release_ex(Z_STR(final_name), 0);
2374 						return -1;
2375 					}
2376 				} else {
2377 					Z_TRY_ADDREF_P(entry);
2378 					zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2379 				}
2380 				count++;
2381 			}
2382 		}
2383 		zval_ptr_dtor_str(&final_name);
2384 	} ZEND_HASH_FOREACH_END();
2385 
2386 	return count;
2387 }
2388 /* }}} */
2389 
2390 static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2391 {
2392 	zend_long count = 0;
2393 	zend_string *var_name;
2394 	zend_ulong num_key;
2395 	zval *entry, *orig_var, final_name;
2396 
2397 	ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2398 		if (var_name) {
2399 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2400 			 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2401 				php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2402 				if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2403 					zval_ptr_dtor_str(&final_name);
2404 					continue;
2405 				}
2406 			} else {
2407 				ZVAL_STR_COPY(&final_name, var_name);
2408 			}
2409 		} else {
2410 			zend_string *str = zend_long_to_str(num_key);
2411 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2412 			zend_string_release_ex(str, 0);
2413 			if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2414 				zval_ptr_dtor_str(&final_name);
2415 				continue;
2416 			}
2417 		}
2418 		if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2419 			zend_throw_error(NULL, "Cannot re-assign $this");
2420 			return -1;
2421 		} else {
2422 			if (Z_ISREF_P(entry)) {
2423 				Z_ADDREF_P(entry);
2424 			} else {
2425 				ZVAL_MAKE_REF_EX(entry, 2);
2426 			}
2427 			if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2428 				if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2429 					orig_var = Z_INDIRECT_P(orig_var);
2430 				}
2431 				zval_ptr_dtor(orig_var);
2432 				ZVAL_REF(orig_var, Z_REF_P(entry));
2433 			} else {
2434 				zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2435 			}
2436 			count++;
2437 		}
2438 		zval_ptr_dtor_str(&final_name);
2439 	} ZEND_HASH_FOREACH_END();
2440 
2441 	return count;
2442 }
2443 /* }}} */
2444 
2445 static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2446 {
2447 	zend_long count = 0;
2448 	zend_string *var_name;
2449 	zend_ulong num_key;
2450 	zval *entry, *orig_var, final_name;
2451 
2452 	ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2453 		if (var_name) {
2454 			if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2455 			 || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2456 				php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2457 				if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2458 					zval_ptr_dtor_str(&final_name);
2459 					continue;
2460 				}
2461 			} else {
2462 				ZVAL_STR_COPY(&final_name, var_name);
2463 			}
2464 		} else {
2465 			zend_string *str = zend_long_to_str(num_key);
2466 			php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2467 			zend_string_release_ex(str, 0);
2468 			if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2469 				zval_ptr_dtor_str(&final_name);
2470 				continue;
2471 			}
2472 		}
2473 		if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) {
2474 			zend_throw_error(NULL, "Cannot re-assign $this");
2475 			return -1;
2476 		} else {
2477 			ZVAL_DEREF(entry);
2478 			if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2479 				if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2480 					orig_var = Z_INDIRECT_P(orig_var);
2481 				}
2482 				ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2483 				if (UNEXPECTED(EG(exception))) {
2484 					zend_string_release_ex(Z_STR(final_name), 0);
2485 					return -1;
2486 				}
2487 			} else {
2488 				Z_TRY_ADDREF_P(entry);
2489 				zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2490 			}
2491 			count++;
2492 		}
2493 		zval_ptr_dtor_str(&final_name);
2494 	} ZEND_HASH_FOREACH_END();
2495 
2496 	return count;
2497 }
2498 /* }}} */
2499 
2500 static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2501 {
2502 	zend_long count = 0;
2503 	zend_string *var_name;
2504 	zval *entry, *orig_var;
2505 
2506 	if (HT_IS_PACKED(arr)) {
2507 		return 0;
2508 	}
2509 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2510 		if (!var_name) {
2511 			continue;
2512 		}
2513 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2514 			continue;
2515 		}
2516 		if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2517 			continue;
2518 		}
2519 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2520 		if (orig_var) {
2521 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2522 				orig_var = Z_INDIRECT_P(orig_var);
2523 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2524 					if (Z_ISREF_P(entry)) {
2525 						Z_ADDREF_P(entry);
2526 					} else {
2527 						ZVAL_MAKE_REF_EX(entry, 2);
2528 					}
2529 					ZVAL_REF(orig_var, Z_REF_P(entry));
2530 					count++;
2531 				}
2532 			}
2533 		} else {
2534 			if (Z_ISREF_P(entry)) {
2535 				Z_ADDREF_P(entry);
2536 			} else {
2537 				ZVAL_MAKE_REF_EX(entry, 2);
2538 			}
2539 			zend_hash_add_new(symbol_table, var_name, entry);
2540 			count++;
2541 		}
2542 	} ZEND_HASH_FOREACH_END();
2543 
2544 	return count;
2545 }
2546 /* }}} */
2547 
2548 static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2549 {
2550 	zend_long count = 0;
2551 	zend_string *var_name;
2552 	zval *entry, *orig_var;
2553 
2554 	if (HT_IS_PACKED(arr)) {
2555 		return 0;
2556 	}
2557 	ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2558 		if (!var_name) {
2559 			continue;
2560 		}
2561 		if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2562 			continue;
2563 		}
2564 		if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2565 			continue;
2566 		}
2567 		orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2568 		if (orig_var) {
2569 			if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2570 				orig_var = Z_INDIRECT_P(orig_var);
2571 				if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2572 					ZVAL_COPY_DEREF(orig_var, entry);
2573 					count++;
2574 				}
2575 			}
2576 		} else {
2577 			ZVAL_DEREF(entry);
2578 			Z_TRY_ADDREF_P(entry);
2579 			zend_hash_add_new(symbol_table, var_name, entry);
2580 			count++;
2581 		}
2582 	} ZEND_HASH_FOREACH_END();
2583 
2584 	return count;
2585 }
2586 /* }}} */
2587 
2588 /* {{{ Imports variables into symbol table from an array */
2589 PHP_FUNCTION(extract)
2590 {
2591 	zval *var_array_param;
2592 	zend_long extract_refs;
2593 	zend_long extract_type = PHP_EXTR_OVERWRITE;
2594 	zend_string *prefix = NULL;
2595 	zend_long count;
2596 	zend_array *symbol_table;
2597 
2598 	ZEND_PARSE_PARAMETERS_START(1, 3)
2599 		Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2600 		Z_PARAM_OPTIONAL
2601 		Z_PARAM_LONG(extract_type)
2602 		Z_PARAM_STR(prefix)
2603 	ZEND_PARSE_PARAMETERS_END();
2604 
2605 	extract_refs = (extract_type & PHP_EXTR_REFS);
2606 	if (extract_refs) {
2607 		SEPARATE_ARRAY(var_array_param);
2608 	}
2609 	extract_type &= 0xff;
2610 
2611 	if (extract_type < PHP_EXTR_OVERWRITE || extract_type > PHP_EXTR_IF_EXISTS) {
2612 		zend_argument_value_error(2, "must be a valid extract type");
2613 		RETURN_THROWS();
2614 	}
2615 
2616 	if (extract_type > PHP_EXTR_SKIP && extract_type <= PHP_EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2617 		zend_argument_value_error(3, "is required when using this extract type");
2618 		RETURN_THROWS();
2619 	}
2620 
2621 	if (prefix) {
2622 		if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
2623 			zend_argument_value_error(3, "must be a valid identifier");
2624 			RETURN_THROWS();
2625 		}
2626 	}
2627 
2628 	if (zend_forbid_dynamic_call() == FAILURE) {
2629 		return;
2630 	}
2631 
2632 	symbol_table = zend_rebuild_symbol_table();
2633 	ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2634 
2635 	if (extract_refs) {
2636 		switch (extract_type) {
2637 			case PHP_EXTR_IF_EXISTS:
2638 				count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2639 				break;
2640 			case PHP_EXTR_OVERWRITE:
2641 				count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2642 				break;
2643 			case PHP_EXTR_PREFIX_IF_EXISTS:
2644 				count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2645 				break;
2646 			case PHP_EXTR_PREFIX_SAME:
2647 				count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2648 				break;
2649 			case PHP_EXTR_PREFIX_ALL:
2650 				count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2651 				break;
2652 			case PHP_EXTR_PREFIX_INVALID:
2653 				count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2654 				break;
2655 			default:
2656 				count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2657 				break;
2658 		}
2659 	} else {
2660 		/* The array might be stored in a local variable that will be overwritten */
2661 		zval array_copy;
2662 		ZVAL_COPY(&array_copy, var_array_param);
2663 		switch (extract_type) {
2664 			case PHP_EXTR_IF_EXISTS:
2665 				count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2666 				break;
2667 			case PHP_EXTR_OVERWRITE:
2668 				count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2669 				break;
2670 			case PHP_EXTR_PREFIX_IF_EXISTS:
2671 				count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2672 				break;
2673 			case PHP_EXTR_PREFIX_SAME:
2674 				count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2675 				break;
2676 			case PHP_EXTR_PREFIX_ALL:
2677 				count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2678 				break;
2679 			case PHP_EXTR_PREFIX_INVALID:
2680 				count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2681 				break;
2682 			default:
2683 				count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2684 				break;
2685 		}
2686 		zval_ptr_dtor(&array_copy);
2687 	}
2688 
2689 	RETURN_LONG(count);
2690 }
2691 /* }}} */
2692 
2693 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2694 {
2695 	zval *value_ptr, data;
2696 
2697 	ZVAL_DEREF(entry);
2698 	if (Z_TYPE_P(entry) == IS_STRING) {
2699 		if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2700 			ZVAL_DEREF(value_ptr);
2701 			Z_TRY_ADDREF_P(value_ptr);
2702 			zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2703 		} else if (zend_string_equals(Z_STR_P(entry), ZSTR_KNOWN(ZEND_STR_THIS))) {
2704 			zend_object *object = zend_get_this_object(EG(current_execute_data));
2705 			if (object) {
2706 				ZVAL_OBJ_COPY(&data, object);
2707 				zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2708 			}
2709 		} else {
2710 			php_error_docref_unchecked(NULL, E_WARNING, "Undefined variable $%S", Z_STR_P(entry));
2711 		}
2712 	} else if (Z_TYPE_P(entry) == IS_ARRAY) {
2713 		if (Z_REFCOUNTED_P(entry)) {
2714 			if (Z_IS_RECURSIVE_P(entry)) {
2715 				zend_throw_error(NULL, "Recursion detected");
2716 				return;
2717 			}
2718 			Z_PROTECT_RECURSION_P(entry);
2719 		}
2720 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
2721 			php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
2722 		} ZEND_HASH_FOREACH_END();
2723 		if (Z_REFCOUNTED_P(entry)) {
2724 			Z_UNPROTECT_RECURSION_P(entry);
2725 		}
2726 	} else {
2727 		php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2728 		return;
2729 	}
2730 }
2731 /* }}} */
2732 
2733 /* {{{ Creates a hash containing variables and their values */
2734 PHP_FUNCTION(compact)
2735 {
2736 	zval *args = NULL;	/* function arguments array */
2737 	uint32_t num_args, i;
2738 	zend_array *symbol_table;
2739 
2740 	ZEND_PARSE_PARAMETERS_START(1, -1)
2741 		Z_PARAM_VARIADIC('+', args, num_args)
2742 	ZEND_PARSE_PARAMETERS_END();
2743 
2744 	if (zend_forbid_dynamic_call() == FAILURE) {
2745 		return;
2746 	}
2747 
2748 	symbol_table = zend_rebuild_symbol_table();
2749 	ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2750 
2751 	/* compact() is probably most used with a single array of var_names
2752 	   or multiple string names, rather than a combination of both.
2753 	   So quickly guess a minimum result size based on that */
2754 	if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2755 		array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2756 	} else {
2757 		array_init_size(return_value, num_args);
2758 	}
2759 
2760 	for (i = 0; i < num_args; i++) {
2761 		php_compact_var(symbol_table, return_value, &args[i], i + 1);
2762 	}
2763 }
2764 /* }}} */
2765 
2766 /* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2767 PHP_FUNCTION(array_fill)
2768 {
2769 	zval *val;
2770 	zend_long start_key, num;
2771 
2772 	ZEND_PARSE_PARAMETERS_START(3, 3)
2773 		Z_PARAM_LONG(start_key)
2774 		Z_PARAM_LONG(num)
2775 		Z_PARAM_ZVAL(val)
2776 	ZEND_PARSE_PARAMETERS_END();
2777 
2778 	if (EXPECTED(num > 0)) {
2779 		if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2780 			zend_argument_value_error(2, "is too large");
2781 			RETURN_THROWS();
2782 		} else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2783 			zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2784 			RETURN_THROWS();
2785 		} else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2786 			/* create packed array */
2787 			zval *zv;
2788 
2789 			array_init_size(return_value, (uint32_t)(start_key + num));
2790 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2791 			Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2792 			Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2793 			Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2794 
2795 			if (Z_REFCOUNTED_P(val)) {
2796 				GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2797 			}
2798 
2799 			zv = Z_ARRVAL_P(return_value)->arPacked;
2800 
2801 			while (start_key--) {
2802 				ZVAL_UNDEF(zv);
2803 				zv++;
2804 			}
2805 			while (num--) {
2806 				ZVAL_COPY_VALUE(zv, val);
2807 				zv++;
2808 			}
2809 		} else {
2810 			/* create hash */
2811 			array_init_size(return_value, (uint32_t)num);
2812 			zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2813 			if (Z_REFCOUNTED_P(val)) {
2814 				GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2815 			}
2816 			zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2817 			while (--num) {
2818 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2819 				start_key++;
2820 			}
2821 		}
2822 	} else if (EXPECTED(num == 0)) {
2823 		RETURN_EMPTY_ARRAY();
2824 	} else {
2825 		zend_argument_value_error(2, "must be greater than or equal to 0");
2826 		RETURN_THROWS();
2827 	}
2828 }
2829 /* }}} */
2830 
2831 /* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2832 PHP_FUNCTION(array_fill_keys)
2833 {
2834 	zval *keys, *val, *entry;
2835 
2836 	ZEND_PARSE_PARAMETERS_START(2, 2)
2837 		Z_PARAM_ARRAY(keys)
2838 		Z_PARAM_ZVAL(val)
2839 	ZEND_PARSE_PARAMETERS_END();
2840 
2841 	/* Initialize return array */
2842 	array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2843 
2844 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2845 		ZVAL_DEREF(entry);
2846 		Z_TRY_ADDREF_P(val);
2847 		if (Z_TYPE_P(entry) == IS_LONG) {
2848 			zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2849 		} else {
2850 			zend_string *tmp_key;
2851 			zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2852 			zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2853 			zend_tmp_string_release(tmp_key);
2854 		}
2855 	} ZEND_HASH_FOREACH_END();
2856 }
2857 /* }}} */
2858 
2859 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2860 		double __calc_size = ((start - end) / (_step)) + 1; \
2861 		if (__calc_size >= (double)HT_MAX_SIZE) { \
2862 			double __exceed_by = __calc_size - (double)HT_MAX_SIZE; \
2863 			zend_value_error(\
2864 				"The supplied range exceeds the maximum array size by %.1f elements: " \
2865 				"start=%.1f, end=%.1f, step=%.1f. Max size: %.0f", \
2866 				__exceed_by, end, start, (_step), (double)HT_MAX_SIZE); \
2867 			RETURN_THROWS(); \
2868 		} \
2869 		size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2870 		array_init_size(return_value, size); \
2871 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2872 	} while (0)
2873 
2874 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2875 		zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2876 		if (__calc_size >= HT_MAX_SIZE - 1) { \
2877 			uint64_t __excess = __calc_size - (HT_MAX_SIZE - 1); \
2878 			zend_value_error(\
2879 				"The supplied range exceeds the maximum array size by %" PRIu64 " elements: " \
2880 				"start=" ZEND_LONG_FMT ", end=" ZEND_LONG_FMT ", step=" ZEND_LONG_FMT ". " \
2881 				"Calculated size: %" PRIu64 ". Maximum size: %" PRIu64 ".", \
2882 				__excess, end, start, (_step), (uint64_t)__calc_size, (uint64_t)HT_MAX_SIZE); \
2883 			RETURN_THROWS(); \
2884 		} \
2885 		size = (uint32_t)(__calc_size + 1); \
2886 		array_init_size(return_value, size); \
2887 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2888 	} while (0)
2889 
2890 /* Process input for the range() function
2891  * 0 on exceptions
2892  * IS_LONG if only interpretable as int
2893  * IS_DOUBLE if only interpretable as float
2894  * IS_STRING if only interpretable as string
2895  * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
2896 static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2897 {
2898 	switch (Z_TYPE_P(input)) {
2899 		case IS_LONG:
2900 			*lval = Z_LVAL_P(input);
2901 			*dval = (double) Z_LVAL_P(input);
2902 			return IS_LONG;
2903 		case IS_DOUBLE:
2904 			*dval = Z_DVAL_P(input);
2905 			check_dval_value:
2906 			if (zend_isinf(*dval)) {
2907 				zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2908 				return 0;
2909 			}
2910 			if (zend_isnan(*dval)) {
2911 				zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2912 				return 0;
2913 			}
2914 			return IS_DOUBLE;
2915 		case IS_STRING: {
2916 			/* Process strings:
2917 			 * - Empty strings are converted to 0 with a diagnostic
2918 			 * - Check if string is numeric and store the values in passed pointer
2919 			 * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2920 			 * - If numeric int, check it is one byte or not
2921 			 *   - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2922 			 *   - If not should only be interpreted as int, return IS_LONG;
2923 			 * - Otherwise is a string and return IS_STRING */
2924 			if (Z_STRLEN_P(input) == 0) {
2925 				const char *arg_name = get_active_function_arg_name(arg_num);
2926 				php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2927 				if (UNEXPECTED(EG(exception))) {
2928 					return 0;
2929 				}
2930 				*lval = 0;
2931 				*dval = 0.0;
2932 				return IS_LONG;
2933 			}
2934 			uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2935 			if (type == IS_DOUBLE) {
2936 				goto check_dval_value;
2937 			}
2938 			if (type == IS_LONG) {
2939 				*dval = (double) *lval;
2940 				if (Z_STRLEN_P(input) == 1) {
2941 					return IS_ARRAY;
2942 				} else {
2943 					return IS_LONG;
2944 				}
2945 			}
2946 			if (Z_STRLEN_P(input) != 1) {
2947 				const char *arg_name = get_active_function_arg_name(arg_num);
2948 				php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2949 				if (UNEXPECTED(EG(exception))) {
2950 					return 0;
2951 				}
2952 			}
2953 			/* Set fall back values to 0 in case the other argument is not a string */
2954 			*lval = 0;
2955 			*dval = 0.0;
2956 			return IS_STRING;
2957 		}
2958 		EMPTY_SWITCH_DEFAULT_CASE();
2959 	}
2960 }
2961 
2962 /* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2963 PHP_FUNCTION(range)
2964 {
2965 	zval *user_start, *user_end, *user_step = NULL, tmp;
2966 	bool is_step_double = false;
2967 	bool is_step_negative = false;
2968 	double step_double = 1.0;
2969 	zend_long step = 1;
2970 
2971 	ZEND_PARSE_PARAMETERS_START(2, 3)
2972 		Z_PARAM_NUMBER_OR_STR(user_start)
2973 		Z_PARAM_NUMBER_OR_STR(user_end)
2974 		Z_PARAM_OPTIONAL
2975 		Z_PARAM_NUMBER(user_step)
2976 	ZEND_PARSE_PARAMETERS_END();
2977 
2978 	if (user_step) {
2979 		if (UNEXPECTED(Z_TYPE_P(user_step) == IS_DOUBLE)) {
2980 			step_double = Z_DVAL_P(user_step);
2981 
2982 			if (zend_isinf(step_double)) {
2983 				zend_argument_value_error(3, "must be a finite number, INF provided");
2984 				RETURN_THROWS();
2985 			}
2986 			if (zend_isnan(step_double)) {
2987 				zend_argument_value_error(3, "must be a finite number, NAN provided");
2988 				RETURN_THROWS();
2989 			}
2990 
2991 			/* We only want positive step values. */
2992 			if (step_double < 0.0) {
2993 				is_step_negative = true;
2994 				step_double *= -1;
2995 			}
2996 			step = zend_dval_to_lval(step_double);
2997 			if (!zend_is_long_compatible(step_double, step)) {
2998 				is_step_double = true;
2999 			}
3000 		} else {
3001 			step = Z_LVAL_P(user_step);
3002 			/* We only want positive step values. */
3003 			if (step < 0) {
3004 				if (UNEXPECTED(step == ZEND_LONG_MIN)) {
3005 					zend_argument_value_error(3, "must be greater than " ZEND_LONG_FMT, step);
3006 					RETURN_THROWS();
3007 				}
3008 				is_step_negative = true;
3009 				step *= -1;
3010 			}
3011 			step_double = (double) step;
3012 		}
3013 		if (step_double == 0.0) {
3014 			zend_argument_value_error(3, "cannot be 0");
3015 			RETURN_THROWS();
3016 		}
3017 	}
3018 
3019 	uint8_t start_type;
3020 	double start_double;
3021 	zend_long start_long;
3022 	uint8_t end_type;
3023 	double end_double;
3024 	zend_long end_long;
3025 
3026 	start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
3027 	if (start_type == 0) {
3028 		RETURN_THROWS();
3029 	}
3030 	end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
3031 	if (end_type == 0) {
3032 		RETURN_THROWS();
3033 	}
3034 
3035 	/* If the range is given as strings, generate an array of characters. */
3036 	if (start_type >= IS_STRING || end_type >= IS_STRING) {
3037 		/* If one of the inputs is NOT a string nor single-byte string */
3038 		if (UNEXPECTED(start_type < IS_STRING || end_type < IS_STRING)) {
3039 			if (start_type < IS_STRING) {
3040 				if (end_type != IS_ARRAY) {
3041 					php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if"
3042 						" argument #2 ($end) is a single byte string, argument #2 ($end) converted to 0");
3043 				}
3044 				end_type = IS_LONG;
3045 			} else if (end_type < IS_STRING) {
3046 				if (start_type != IS_ARRAY) {
3047 					php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a single byte string if"
3048 						" argument #1 ($start) is a single byte string, argument #1 ($start) converted to 0");
3049 				}
3050 				start_type = IS_LONG;
3051 			}
3052 			if (UNEXPECTED(EG(exception))) {
3053 				RETURN_THROWS();
3054 			}
3055 			goto handle_numeric_inputs;
3056 		}
3057 
3058 		if (is_step_double) {
3059 			/* Only emit warning if one of the input is not a numeric digit */
3060 			if (start_type == IS_STRING || end_type == IS_STRING) {
3061 				php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
3062 					" of characters, inputs converted to 0");
3063 			}
3064 			if (UNEXPECTED(EG(exception))) {
3065 				RETURN_THROWS();
3066 			}
3067 			end_type = IS_LONG;
3068 			start_type = IS_LONG;
3069 			goto handle_numeric_inputs;
3070 		}
3071 
3072 		/* Generate array of characters */
3073 		unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
3074 		unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
3075 
3076 		/* Decreasing char range */
3077 		if (low > high) {
3078 			if (low - high < step) {
3079 				goto boundary_error;
3080 			}
3081 			/* Initialize the return_value as an array. */
3082 			array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
3083 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3084 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3085 				for (; low >= high; low -= (unsigned int)step) {
3086 					ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3087 					ZEND_HASH_FILL_NEXT();
3088 					if (((signed int)low - step) < 0) {
3089 						break;
3090 					}
3091 				}
3092 			} ZEND_HASH_FILL_END();
3093 		} else if (high > low) { /* Increasing char range */
3094 			if (is_step_negative) {
3095 				goto negative_step_error;
3096 			}
3097 			if (high - low < step) {
3098 				goto boundary_error;
3099 			}
3100 			array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
3101 			zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3102 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3103 				for (; low <= high; low += (unsigned int)step) {
3104 					ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3105 					ZEND_HASH_FILL_NEXT();
3106 					if (((signed int)low + step) > 255) {
3107 						break;
3108 					}
3109 				}
3110 			} ZEND_HASH_FILL_END();
3111 		} else {
3112 			array_init(return_value);
3113 			ZVAL_CHAR(&tmp, low);
3114 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3115 		}
3116 		return;
3117 	}
3118 
3119 	handle_numeric_inputs:
3120 	if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
3121 		double element;
3122 		uint32_t i, size;
3123 
3124 		/* Decreasing float range */
3125 		if (start_double > end_double) {
3126 			if (start_double - end_double < step_double) {
3127 				goto boundary_error;
3128 			}
3129 
3130 			RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
3131 
3132 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3133 				for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
3134 					ZEND_HASH_FILL_SET_DOUBLE(element);
3135 					ZEND_HASH_FILL_NEXT();
3136 				}
3137 			} ZEND_HASH_FILL_END();
3138 		} else if (end_double > start_double) { /* Increasing float range */
3139 			if (is_step_negative) {
3140 				goto negative_step_error;
3141 			}
3142 			if (end_double - start_double < step_double) {
3143 				goto boundary_error;
3144 			}
3145 
3146 			RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
3147 
3148 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3149 				for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
3150 					ZEND_HASH_FILL_SET_DOUBLE(element);
3151 					ZEND_HASH_FILL_NEXT();
3152 				}
3153 			} ZEND_HASH_FILL_END();
3154 		} else {
3155 			array_init(return_value);
3156 			ZVAL_DOUBLE(&tmp, start_double);
3157 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3158 		}
3159 	} else {
3160 		ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
3161 		/* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
3162 		zend_ulong unsigned_step= (zend_ulong)step;
3163 		uint32_t i, size;
3164 
3165 		/* Decreasing int range */
3166 		if (start_long > end_long) {
3167 			if ((zend_ulong)start_long - end_long < unsigned_step) {
3168 				goto boundary_error;
3169 			}
3170 
3171 			RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3172 
3173 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3174 				for (i = 0; i < size; ++i) {
3175 					ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3176 					ZEND_HASH_FILL_NEXT();
3177 				}
3178 			} ZEND_HASH_FILL_END();
3179 		} else if (end_long > start_long) { /* Increasing int range */
3180 			if (is_step_negative) {
3181 				goto negative_step_error;
3182 			}
3183 			if ((zend_ulong)end_long - start_long < unsigned_step) {
3184 				goto boundary_error;
3185 			}
3186 
3187 			RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3188 
3189 			ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3190 				for (i = 0; i < size; ++i) {
3191 					ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3192 					ZEND_HASH_FILL_NEXT();
3193 				}
3194 			} ZEND_HASH_FILL_END();
3195 		} else {
3196 			array_init(return_value);
3197 			ZVAL_LONG(&tmp, start_long);
3198 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3199 		}
3200 	}
3201 	return;
3202 
3203 negative_step_error:
3204 	zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3205 	RETURN_THROWS();
3206 
3207 boundary_error:
3208 	zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3209 	RETURN_THROWS();
3210 }
3211 /* }}} */
3212 
3213 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
3214 #undef RANGE_CHECK_LONG_INIT_ARRAY
3215 
3216 /* {{{ php_array_data_shuffle */
3217 PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *array) /* {{{ */
3218 {
3219 	const php_random_algo *algo = engine.algo;
3220 	void *state = engine.state;
3221 
3222 	int64_t idx, j, n_elems, rnd_idx, n_left;
3223 	zval *zv, temp;
3224 	HashTable *hash;
3225 
3226 	n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3227 
3228 	if (n_elems < 1) {
3229 		return true;
3230 	}
3231 
3232 	hash = Z_ARRVAL_P(array);
3233 	n_left = n_elems;
3234 
3235 	if (!HT_IS_PACKED(hash)) {
3236 		if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3237 			Bucket *p = hash->arData;
3238 			zend_long i = hash->nNumUsed;
3239 
3240 			for (; i > 0; p++, i--) {
3241 				if (p->key) {
3242 					zend_string_release(p->key);
3243 					p->key = NULL;
3244 				}
3245 			}
3246 		}
3247 		zend_hash_to_packed(hash);
3248 	}
3249 
3250 	if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3251 		if (hash->nNumUsed != hash->nNumOfElements) {
3252 			for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3253 				zv = hash->arPacked + idx;
3254 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3255 				if (j != idx) {
3256 					ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3257 				}
3258 				j++;
3259 			}
3260 		}
3261 		while (--n_left) {
3262 			rnd_idx = algo->range(state, 0, n_left);
3263 			if (EG(exception)) {
3264 				return false;
3265 			}
3266 			if (rnd_idx != n_left) {
3267 				ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3268 				ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3269 				ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3270 			}
3271 		}
3272 	} else {
3273 		zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3274 
3275 		if (hash->nNumUsed != hash->nNumOfElements) {
3276 			for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3277 				zv = hash->arPacked + idx;
3278 				if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3279 				if (j != idx) {
3280 					ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3281 					if (idx == iter_pos) {
3282 						zend_hash_iterators_update(hash, idx, j);
3283 						iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3284 					}
3285 				}
3286 				j++;
3287 			}
3288 		}
3289 		while (--n_left) {
3290 			rnd_idx = algo->range(state, 0, n_left);
3291 			if (EG(exception)) {
3292 				return false;
3293 			}
3294 			if (rnd_idx != n_left) {
3295 				ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3296 				ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3297 				ZVAL_COPY_VALUE(&hash->