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->