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