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_rand.h"
40 #include "php_math.h"
41 #include "zend_smart_str.h"
42 #include "zend_bitset.h"
43 #include "zend_exceptions.h"
44 #include "ext/spl/spl_array.h"
45
46 /* {{{ defines */
47 #define EXTR_OVERWRITE 0
48 #define EXTR_SKIP 1
49 #define EXTR_PREFIX_SAME 2
50 #define EXTR_PREFIX_ALL 3
51 #define EXTR_PREFIX_INVALID 4
52 #define EXTR_PREFIX_IF_EXISTS 5
53 #define EXTR_IF_EXISTS 6
54
55 #define EXTR_REFS 0x100
56
57 #define CASE_LOWER 0
58 #define CASE_UPPER 1
59
60 #define DIFF_NORMAL 1
61 #define DIFF_KEY 2
62 #define DIFF_ASSOC 6
63 #define DIFF_COMP_DATA_NONE -1
64 #define DIFF_COMP_DATA_INTERNAL 0
65 #define DIFF_COMP_DATA_USER 1
66 #define DIFF_COMP_KEY_INTERNAL 0
67 #define DIFF_COMP_KEY_USER 1
68
69 #define INTERSECT_NORMAL 1
70 #define INTERSECT_KEY 2
71 #define INTERSECT_ASSOC 6
72 #define INTERSECT_COMP_DATA_NONE -1
73 #define INTERSECT_COMP_DATA_INTERNAL 0
74 #define INTERSECT_COMP_DATA_USER 1
75 #define INTERSECT_COMP_KEY_INTERNAL 0
76 #define INTERSECT_COMP_KEY_USER 1
77 /* }}} */
78
ZEND_DECLARE_MODULE_GLOBALS(array)79 ZEND_DECLARE_MODULE_GLOBALS(array)
80
81 /* {{{ php_array_init_globals */
82 static void php_array_init_globals(zend_array_globals *array_globals)
83 {
84 memset(array_globals, 0, sizeof(zend_array_globals));
85 }
86 /* }}} */
87
PHP_MINIT_FUNCTION(array)88 PHP_MINIT_FUNCTION(array) /* {{{ */
89 {
90 ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
91
92 REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
93 REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
94 REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
95 REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
96 REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
97 REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
98 REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
99 REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
100
101 REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
102 REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
103
104 REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
105 REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
106 REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
107 REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
108 REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
109 REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
110
111 REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
112 REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
113
114 REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
115 REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
116
117 REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
118 REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
119
120 return SUCCESS;
121 }
122 /* }}} */
123
PHP_MSHUTDOWN_FUNCTION(array)124 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
125 {
126 #ifdef ZTS
127 ts_free_id(array_globals_id);
128 #endif
129
130 return SUCCESS;
131 }
132 /* }}} */
133
stable_sort_fallback(Bucket * a,Bucket * b)134 static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
135 if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
136 return 1;
137 } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
138 return -1;
139 } else {
140 return 0;
141 }
142 }
143
144 #define RETURN_STABLE_SORT(a, b, result) do { \
145 int _result = (result); \
146 if (EXPECTED(_result)) { \
147 return _result; \
148 } \
149 return stable_sort_fallback((a), (b)); \
150 } while (0)
151
152 /* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
153 #define DEFINE_SORT_VARIANTS(name) \
154 static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
155 return php_array_##name##_unstable_i(a, b); \
156 } \
157 static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
158 RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
159 } \
160 static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
161 return php_array_##name##_unstable(a, b) * -1; \
162 } \
163 static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
164 RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
165 } \
166
php_array_key_compare_unstable_i(Bucket * f,Bucket * s)167 static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
168 {
169 zend_uchar t;
170 zend_long l1, l2;
171 double d;
172
173 if (f->key == NULL) {
174 if (s->key == NULL) {
175 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
176 } else {
177 l1 = (zend_long)f->h;
178 t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
179 if (t == IS_LONG) {
180 /* pass */
181 } else if (t == IS_DOUBLE) {
182 return ZEND_NORMALIZE_BOOL((double)l1 - d);
183 } else {
184 l2 = 0;
185 }
186 }
187 } else {
188 if (s->key) {
189 return zendi_smart_strcmp(f->key, s->key);
190 } else {
191 l2 = (zend_long)s->h;
192 t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
193 if (t == IS_LONG) {
194 /* pass */
195 } else if (t == IS_DOUBLE) {
196 return ZEND_NORMALIZE_BOOL(d - (double)l2);
197 } else {
198 l1 = 0;
199 }
200 }
201 }
202 return ZEND_NORMALIZE_BOOL(l1 - l2);
203 }
204 /* }}} */
205
php_array_key_compare_numeric_unstable_i(Bucket * f,Bucket * s)206 static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
207 {
208 if (f->key == NULL && s->key == NULL) {
209 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
210 } else {
211 double d1, d2;
212 if (f->key) {
213 d1 = zend_strtod(f->key->val, NULL);
214 } else {
215 d1 = (double)(zend_long)f->h;
216 }
217 if (s->key) {
218 d2 = zend_strtod(s->key->val, NULL);
219 } else {
220 d2 = (double)(zend_long)s->h;
221 }
222 return ZEND_NORMALIZE_BOOL(d1 - d2);
223 }
224 }
225 /* }}} */
226
php_array_key_compare_string_case_unstable_i(Bucket * f,Bucket * s)227 static zend_always_inline int php_array_key_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
228 {
229 const char *s1, *s2;
230 size_t l1, l2;
231 char buf1[MAX_LENGTH_OF_LONG + 1];
232 char buf2[MAX_LENGTH_OF_LONG + 1];
233
234 if (f->key) {
235 s1 = f->key->val;
236 l1 = f->key->len;
237 } else {
238 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
239 l1 = buf1 + sizeof(buf1) - 1 - s1;
240 }
241 if (s->key) {
242 s2 = s->key->val;
243 l2 = s->key->len;
244 } else {
245 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
246 l2 = buf2 + sizeof(buf2) - 1 - s1;
247 }
248 return zend_binary_strcasecmp_l(s1, l1, s2, l2);
249 }
250 /* }}} */
251
php_array_key_compare_string_unstable_i(Bucket * f,Bucket * s)252 static zend_always_inline int php_array_key_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
253 {
254 const char *s1, *s2;
255 size_t l1, l2;
256 char buf1[MAX_LENGTH_OF_LONG + 1];
257 char buf2[MAX_LENGTH_OF_LONG + 1];
258
259 if (f->key) {
260 s1 = f->key->val;
261 l1 = f->key->len;
262 } else {
263 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
264 l1 = buf1 + sizeof(buf1) - 1 - s1;
265 }
266 if (s->key) {
267 s2 = s->key->val;
268 l2 = s->key->len;
269 } else {
270 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
271 l2 = buf2 + sizeof(buf2) - 1 - s2;
272 }
273 return zend_binary_strcmp(s1, l1, s2, l2);
274 }
275 /* }}} */
276
php_array_key_compare_string_natural_general(Bucket * f,Bucket * s,int fold_case)277 static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case) /* {{{ */
278 {
279 const char *s1, *s2;
280 size_t l1, l2;
281 char buf1[MAX_LENGTH_OF_LONG + 1];
282 char buf2[MAX_LENGTH_OF_LONG + 1];
283
284 if (f->key) {
285 s1 = f->key->val;
286 l1 = f->key->len;
287 } else {
288 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
289 l1 = buf1 + sizeof(buf1) - 1 - s1;
290 }
291 if (s->key) {
292 s2 = s->key->val;
293 l2 = s->key->len;
294 } else {
295 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
296 l2 = buf2 + sizeof(buf2) - 1 - s1;
297 }
298 return strnatcmp_ex(s1, l1, s2, l2, fold_case);
299 }
300 /* }}} */
301
php_array_key_compare_string_natural_case(Bucket * a,Bucket * b)302 static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
303 {
304 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 1));
305 }
306 /* }}} */
307
php_array_reverse_key_compare_string_natural_case(Bucket * a,Bucket * b)308 static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
309 {
310 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 1));
311 }
312 /* }}} */
313
php_array_key_compare_string_natural(Bucket * a,Bucket * b)314 static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
315 {
316 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 0));
317 }
318 /* }}} */
319
php_array_reverse_key_compare_string_natural(Bucket * a,Bucket * b)320 static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
321 {
322 RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 0));
323 }
324 /* }}} */
325
php_array_key_compare_string_locale_unstable_i(Bucket * f,Bucket * s)326 static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
327 {
328 const char *s1, *s2;
329 char buf1[MAX_LENGTH_OF_LONG + 1];
330 char buf2[MAX_LENGTH_OF_LONG + 1];
331
332 if (f->key) {
333 s1 = f->key->val;
334 } else {
335 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
336 }
337 if (s->key) {
338 s2 = s->key->val;
339 } else {
340 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
341 }
342 return strcoll(s1, s2);
343 }
344 /* }}} */
345
php_array_data_compare_unstable_i(Bucket * f,Bucket * s)346 static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
347 {
348 int result = zend_compare(&f->val, &s->val);
349 /* Special enums handling for array_unique. We don't want to add this logic to zend_compare as
350 * that would be observable via comparison operators. */
351 zval *rhs = &s->val;
352 ZVAL_DEREF(rhs);
353 if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
354 && result == ZEND_UNCOMPARABLE
355 && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
356 zval *lhs = &f->val;
357 ZVAL_DEREF(lhs);
358 if (Z_TYPE_P(lhs) == IS_OBJECT && (Z_OBJCE_P(lhs)->ce_flags & ZEND_ACC_ENUM)) {
359 // Order doesn't matter, we just need to group the same enum values
360 uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
361 uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
362 return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
363 } else {
364 // Shift enums to the end of the array
365 return -1;
366 }
367 }
368 return result;
369 }
370 /* }}} */
371
php_array_data_compare_numeric_unstable_i(Bucket * f,Bucket * s)372 static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
373 {
374 return numeric_compare_function(&f->val, &s->val);
375 }
376 /* }}} */
377
php_array_data_compare_string_case_unstable_i(Bucket * f,Bucket * s)378 static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
379 {
380 return string_case_compare_function(&f->val, &s->val);
381 }
382 /* }}} */
383
php_array_data_compare_string_unstable_i(Bucket * f,Bucket * s)384 static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
385 {
386 return string_compare_function(&f->val, &s->val);
387 }
388 /* }}} */
389
php_array_natural_general_compare(Bucket * f,Bucket * s,int fold_case)390 static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */
391 {
392 zend_string *tmp_str1, *tmp_str2;
393 zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
394 zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
395
396 int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
397
398 zend_tmp_string_release(tmp_str1);
399 zend_tmp_string_release(tmp_str2);
400 return result;
401 }
402 /* }}} */
403
php_array_natural_compare_unstable_i(Bucket * a,Bucket * b)404 static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
405 {
406 return php_array_natural_general_compare(a, b, 0);
407 }
408 /* }}} */
409
php_array_natural_case_compare_unstable_i(Bucket * a,Bucket * b)410 static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
411 {
412 return php_array_natural_general_compare(a, b, 1);
413 }
414 /* }}} */
415
php_array_data_compare_string_locale_unstable_i(Bucket * f,Bucket * s)416 static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
417 {
418 return string_locale_compare_function(&f->val, &s->val);
419 }
420 /* }}} */
421
422 DEFINE_SORT_VARIANTS(key_compare);
423 DEFINE_SORT_VARIANTS(key_compare_numeric);
424 DEFINE_SORT_VARIANTS(key_compare_string_case);
425 DEFINE_SORT_VARIANTS(key_compare_string);
426 DEFINE_SORT_VARIANTS(key_compare_string_locale);
427 DEFINE_SORT_VARIANTS(data_compare);
428 DEFINE_SORT_VARIANTS(data_compare_numeric);
429 DEFINE_SORT_VARIANTS(data_compare_string_case);
430 DEFINE_SORT_VARIANTS(data_compare_string);
431 DEFINE_SORT_VARIANTS(data_compare_string_locale);
432 DEFINE_SORT_VARIANTS(natural_compare);
433 DEFINE_SORT_VARIANTS(natural_case_compare);
434
php_get_key_compare_func(zend_long sort_type,int reverse)435 static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */
436 {
437 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
438 case PHP_SORT_NUMERIC:
439 if (reverse) {
440 return php_array_reverse_key_compare_numeric;
441 } else {
442 return php_array_key_compare_numeric;
443 }
444 break;
445
446 case PHP_SORT_STRING:
447 if (sort_type & PHP_SORT_FLAG_CASE) {
448 if (reverse) {
449 return php_array_reverse_key_compare_string_case;
450 } else {
451 return php_array_key_compare_string_case;
452 }
453 } else {
454 if (reverse) {
455 return php_array_reverse_key_compare_string;
456 } else {
457 return php_array_key_compare_string;
458 }
459 }
460 break;
461
462 case PHP_SORT_NATURAL:
463 if (sort_type & PHP_SORT_FLAG_CASE) {
464 if (reverse) {
465 return php_array_reverse_key_compare_string_natural_case;
466 } else {
467 return php_array_key_compare_string_natural_case;
468 }
469 } else {
470 if (reverse) {
471 return php_array_reverse_key_compare_string_natural;
472 } else {
473 return php_array_key_compare_string_natural;
474 }
475 }
476 break;
477
478 case PHP_SORT_LOCALE_STRING:
479 if (reverse) {
480 return php_array_reverse_key_compare_string_locale;
481 } else {
482 return php_array_key_compare_string_locale;
483 }
484 break;
485
486 case PHP_SORT_REGULAR:
487 default:
488 if (reverse) {
489 return php_array_reverse_key_compare;
490 } else {
491 return php_array_key_compare;
492 }
493 break;
494 }
495 return NULL;
496 }
497 /* }}} */
498
php_get_data_compare_func(zend_long sort_type,int reverse)499 static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */
500 {
501 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
502 case PHP_SORT_NUMERIC:
503 if (reverse) {
504 return php_array_reverse_data_compare_numeric;
505 } else {
506 return php_array_data_compare_numeric;
507 }
508 break;
509
510 case PHP_SORT_STRING:
511 if (sort_type & PHP_SORT_FLAG_CASE) {
512 if (reverse) {
513 return php_array_reverse_data_compare_string_case;
514 } else {
515 return php_array_data_compare_string_case;
516 }
517 } else {
518 if (reverse) {
519 return php_array_reverse_data_compare_string;
520 } else {
521 return php_array_data_compare_string;
522 }
523 }
524 break;
525
526 case PHP_SORT_NATURAL:
527 if (sort_type & PHP_SORT_FLAG_CASE) {
528 if (reverse) {
529 return php_array_reverse_natural_case_compare;
530 } else {
531 return php_array_natural_case_compare;
532 }
533 } else {
534 if (reverse) {
535 return php_array_reverse_natural_compare;
536 } else {
537 return php_array_natural_compare;
538 }
539 }
540 break;
541
542 case PHP_SORT_LOCALE_STRING:
543 if (reverse) {
544 return php_array_reverse_data_compare_string_locale;
545 } else {
546 return php_array_data_compare_string_locale;
547 }
548 break;
549
550 case PHP_SORT_REGULAR:
551 default:
552 if (reverse) {
553 return php_array_reverse_data_compare;
554 } else {
555 return php_array_data_compare;
556 }
557 break;
558 }
559 return NULL;
560 }
561 /* }}} */
562
php_get_data_compare_func_unstable(zend_long sort_type,int reverse)563 static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, int reverse) /* {{{ */
564 {
565 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
566 case PHP_SORT_NUMERIC:
567 if (reverse) {
568 return php_array_reverse_data_compare_numeric_unstable;
569 } else {
570 return php_array_data_compare_numeric_unstable;
571 }
572 break;
573
574 case PHP_SORT_STRING:
575 if (sort_type & PHP_SORT_FLAG_CASE) {
576 if (reverse) {
577 return php_array_reverse_data_compare_string_case_unstable;
578 } else {
579 return php_array_data_compare_string_case_unstable;
580 }
581 } else {
582 if (reverse) {
583 return php_array_reverse_data_compare_string_unstable;
584 } else {
585 return php_array_data_compare_string_unstable;
586 }
587 }
588 break;
589
590 case PHP_SORT_NATURAL:
591 if (sort_type & PHP_SORT_FLAG_CASE) {
592 if (reverse) {
593 return php_array_reverse_natural_case_compare_unstable;
594 } else {
595 return php_array_natural_case_compare_unstable;
596 }
597 } else {
598 if (reverse) {
599 return php_array_reverse_natural_compare_unstable;
600 } else {
601 return php_array_natural_compare_unstable;
602 }
603 }
604 break;
605
606 case PHP_SORT_LOCALE_STRING:
607 if (reverse) {
608 return php_array_reverse_data_compare_string_locale_unstable;
609 } else {
610 return php_array_data_compare_string_locale_unstable;
611 }
612 break;
613
614 case PHP_SORT_REGULAR:
615 default:
616 if (reverse) {
617 return php_array_reverse_data_compare_unstable;
618 } else {
619 return php_array_data_compare_unstable;
620 }
621 break;
622 }
623 return NULL;
624 }
625 /* }}} */
626
627 /* {{{ Sort an array by key value in reverse order */
PHP_FUNCTION(krsort)628 PHP_FUNCTION(krsort)
629 {
630 zval *array;
631 zend_long sort_type = PHP_SORT_REGULAR;
632 bucket_compare_func_t cmp;
633
634 ZEND_PARSE_PARAMETERS_START(1, 2)
635 Z_PARAM_ARRAY_EX(array, 0, 1)
636 Z_PARAM_OPTIONAL
637 Z_PARAM_LONG(sort_type)
638 ZEND_PARSE_PARAMETERS_END();
639
640 cmp = php_get_key_compare_func(sort_type, 1);
641
642 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
643
644 RETURN_TRUE;
645 }
646 /* }}} */
647
648 /* {{{ Sort an array by key */
PHP_FUNCTION(ksort)649 PHP_FUNCTION(ksort)
650 {
651 zval *array;
652 zend_long sort_type = PHP_SORT_REGULAR;
653 bucket_compare_func_t cmp;
654
655 ZEND_PARSE_PARAMETERS_START(1, 2)
656 Z_PARAM_ARRAY_EX(array, 0, 1)
657 Z_PARAM_OPTIONAL
658 Z_PARAM_LONG(sort_type)
659 ZEND_PARSE_PARAMETERS_END();
660
661 cmp = php_get_key_compare_func(sort_type, 0);
662
663 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
664
665 RETURN_TRUE;
666 }
667 /* }}} */
668
php_count_recursive(HashTable * ht)669 PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
670 {
671 zend_long cnt = 0;
672 zval *element;
673
674 if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
675 if (GC_IS_RECURSIVE(ht)) {
676 php_error_docref(NULL, E_WARNING, "Recursion detected");
677 return 0;
678 }
679 GC_PROTECT_RECURSION(ht);
680 }
681
682 cnt = zend_hash_num_elements(ht);
683 ZEND_HASH_FOREACH_VAL(ht, element) {
684 ZVAL_DEREF(element);
685 if (Z_TYPE_P(element) == IS_ARRAY) {
686 cnt += php_count_recursive(Z_ARRVAL_P(element));
687 }
688 } ZEND_HASH_FOREACH_END();
689
690 GC_TRY_UNPROTECT_RECURSION(ht);
691 return cnt;
692 }
693 /* }}} */
694
695 /* {{{ Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)696 PHP_FUNCTION(count)
697 {
698 zval *array;
699 zend_long mode = COUNT_NORMAL;
700 zend_long cnt;
701
702 ZEND_PARSE_PARAMETERS_START(1, 2)
703 Z_PARAM_ZVAL(array)
704 Z_PARAM_OPTIONAL
705 Z_PARAM_LONG(mode)
706 ZEND_PARSE_PARAMETERS_END();
707
708 if (mode != COUNT_NORMAL && mode != COUNT_RECURSIVE) {
709 zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
710 RETURN_THROWS();
711 }
712
713 switch (Z_TYPE_P(array)) {
714 case IS_ARRAY:
715 if (mode != COUNT_RECURSIVE) {
716 cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
717 } else {
718 cnt = php_count_recursive(Z_ARRVAL_P(array));
719 }
720 RETURN_LONG(cnt);
721 break;
722 case IS_OBJECT: {
723 zval retval;
724 /* first, we check if the handler is defined */
725 if (Z_OBJ_HT_P(array)->count_elements) {
726 RETVAL_LONG(1);
727 if (SUCCESS == Z_OBJ_HT(*array)->count_elements(Z_OBJ_P(array), &Z_LVAL_P(return_value))) {
728 return;
729 }
730 if (EG(exception)) {
731 RETURN_THROWS();
732 }
733 }
734 /* if not and the object implements Countable we call its count() method */
735 if (instanceof_function(Z_OBJCE_P(array), zend_ce_countable)) {
736 zend_call_method_with_0_params(Z_OBJ_P(array), NULL, NULL, "count", &retval);
737 if (Z_TYPE(retval) != IS_UNDEF) {
738 RETVAL_LONG(zval_get_long(&retval));
739 zval_ptr_dtor(&retval);
740 }
741 return;
742 }
743 }
744 ZEND_FALLTHROUGH;
745 default:
746 zend_argument_type_error(1, "must be of type Countable|array, %s given", zend_zval_type_name(array));
747 RETURN_THROWS();
748 }
749 }
750 /* }}} */
751
php_natsort(INTERNAL_FUNCTION_PARAMETERS,int fold_case)752 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
753 {
754 zval *array;
755
756 ZEND_PARSE_PARAMETERS_START(1, 1)
757 Z_PARAM_ARRAY_EX(array, 0, 1)
758 ZEND_PARSE_PARAMETERS_END();
759
760 if (fold_case) {
761 zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
762 } else {
763 zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
764 }
765
766 RETURN_TRUE;
767 }
768 /* }}} */
769
770 /* {{{ Sort an array using natural sort */
PHP_FUNCTION(natsort)771 PHP_FUNCTION(natsort)
772 {
773 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
774 }
775 /* }}} */
776
777 /* {{{ Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)778 PHP_FUNCTION(natcasesort)
779 {
780 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
781 }
782 /* }}} */
783
784 /* {{{ Sort an array and maintain index association */
PHP_FUNCTION(asort)785 PHP_FUNCTION(asort)
786 {
787 zval *array;
788 zend_long sort_type = PHP_SORT_REGULAR;
789 bucket_compare_func_t cmp;
790
791 ZEND_PARSE_PARAMETERS_START(1, 2)
792 Z_PARAM_ARRAY_EX(array, 0, 1)
793 Z_PARAM_OPTIONAL
794 Z_PARAM_LONG(sort_type)
795 ZEND_PARSE_PARAMETERS_END();
796
797 cmp = php_get_data_compare_func(sort_type, 0);
798
799 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
800
801 RETURN_TRUE;
802 }
803 /* }}} */
804
805 /* {{{ Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)806 PHP_FUNCTION(arsort)
807 {
808 zval *array;
809 zend_long sort_type = PHP_SORT_REGULAR;
810 bucket_compare_func_t cmp;
811
812 ZEND_PARSE_PARAMETERS_START(1, 2)
813 Z_PARAM_ARRAY_EX(array, 0, 1)
814 Z_PARAM_OPTIONAL
815 Z_PARAM_LONG(sort_type)
816 ZEND_PARSE_PARAMETERS_END();
817
818 cmp = php_get_data_compare_func(sort_type, 1);
819
820 zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
821
822 RETURN_TRUE;
823 }
824 /* }}} */
825
826 /* {{{ Sort an array */
PHP_FUNCTION(sort)827 PHP_FUNCTION(sort)
828 {
829 zval *array;
830 zend_long sort_type = PHP_SORT_REGULAR;
831 bucket_compare_func_t cmp;
832
833 ZEND_PARSE_PARAMETERS_START(1, 2)
834 Z_PARAM_ARRAY_EX(array, 0, 1)
835 Z_PARAM_OPTIONAL
836 Z_PARAM_LONG(sort_type)
837 ZEND_PARSE_PARAMETERS_END();
838
839 cmp = php_get_data_compare_func(sort_type, 0);
840
841 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
842
843 RETURN_TRUE;
844 }
845 /* }}} */
846
847 /* {{{ Sort an array in reverse order */
PHP_FUNCTION(rsort)848 PHP_FUNCTION(rsort)
849 {
850 zval *array;
851 zend_long sort_type = PHP_SORT_REGULAR;
852 bucket_compare_func_t cmp;
853
854 ZEND_PARSE_PARAMETERS_START(1, 2)
855 Z_PARAM_ARRAY_EX(array, 0, 1)
856 Z_PARAM_OPTIONAL
857 Z_PARAM_LONG(sort_type)
858 ZEND_PARSE_PARAMETERS_END();
859
860 cmp = php_get_data_compare_func(sort_type, 1);
861
862 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
863
864 RETURN_TRUE;
865 }
866 /* }}} */
867
php_array_user_compare_unstable(Bucket * f,Bucket * s)868 static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
869 {
870 zval args[2];
871 zval retval;
872 bool call_failed;
873
874 ZVAL_COPY(&args[0], &f->val);
875 ZVAL_COPY(&args[1], &s->val);
876
877 BG(user_compare_fci).param_count = 2;
878 BG(user_compare_fci).params = args;
879 BG(user_compare_fci).retval = &retval;
880 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
881 zval_ptr_dtor(&args[1]);
882 zval_ptr_dtor(&args[0]);
883 if (UNEXPECTED(call_failed)) {
884 return 0;
885 }
886
887 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
888 if (!ARRAYG(compare_deprecation_thrown)) {
889 php_error_docref(NULL, E_DEPRECATED,
890 "Returning bool from comparison function is deprecated, "
891 "return an integer less than, equal to, or greater than zero");
892 ARRAYG(compare_deprecation_thrown) = 1;
893 }
894
895 if (Z_TYPE(retval) == IS_FALSE) {
896 /* Retry with swapped operands. */
897 ZVAL_COPY(&args[0], &s->val);
898 ZVAL_COPY(&args[1], &f->val);
899 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
900 zval_ptr_dtor(&args[1]);
901 zval_ptr_dtor(&args[0]);
902 if (call_failed) {
903 return 0;
904 }
905
906 zend_long ret = zval_get_long(&retval);
907 zval_ptr_dtor(&retval);
908 return -ZEND_NORMALIZE_BOOL(ret);
909 }
910 }
911
912 zend_long ret = zval_get_long(&retval);
913 zval_ptr_dtor(&retval);
914 return ZEND_NORMALIZE_BOOL(ret);
915 }
916 /* }}} */
917
php_array_user_compare(Bucket * a,Bucket * b)918 static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
919 {
920 RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
921 }
922 /* }}} */
923
924 #define PHP_ARRAY_CMP_FUNC_VARS \
925 zend_fcall_info old_user_compare_fci; \
926 zend_fcall_info_cache old_user_compare_fci_cache \
927
928 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
929 old_user_compare_fci = BG(user_compare_fci); \
930 old_user_compare_fci_cache = BG(user_compare_fci_cache); \
931 ARRAYG(compare_deprecation_thrown) = 0; \
932 BG(user_compare_fci_cache) = empty_fcall_info_cache; \
933
934 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
935 BG(user_compare_fci) = old_user_compare_fci; \
936 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
937
php_usort(INTERNAL_FUNCTION_PARAMETERS,bucket_compare_func_t compare_func,bool renumber)938 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, bool renumber) /* {{{ */
939 {
940 zval *array;
941 zend_array *arr;
942 PHP_ARRAY_CMP_FUNC_VARS;
943
944 PHP_ARRAY_CMP_FUNC_BACKUP();
945
946 ZEND_PARSE_PARAMETERS_START(2, 2)
947 Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
948 Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
949 ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
950
951 arr = Z_ARR_P(array);
952 if (zend_hash_num_elements(arr) == 0) {
953 PHP_ARRAY_CMP_FUNC_RESTORE();
954 RETURN_TRUE;
955 }
956
957 /* Copy array, so the in-place modifications will not be visible to the callback function */
958 arr = zend_array_dup(arr);
959
960 zend_hash_sort(arr, compare_func, renumber);
961
962 zval garbage;
963 ZVAL_COPY_VALUE(&garbage, array);
964 ZVAL_ARR(array, arr);
965 zval_ptr_dtor(&garbage);
966
967 PHP_ARRAY_CMP_FUNC_RESTORE();
968 RETURN_TRUE;
969 }
970 /* }}} */
971
972 /* {{{ Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)973 PHP_FUNCTION(usort)
974 {
975 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
976 }
977 /* }}} */
978
979 /* {{{ Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)980 PHP_FUNCTION(uasort)
981 {
982 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
983 }
984 /* }}} */
985
php_array_user_key_compare_unstable(Bucket * f,Bucket * s)986 static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
987 {
988 zval args[2];
989 zval retval;
990 bool call_failed;
991
992 if (f->key == NULL) {
993 ZVAL_LONG(&args[0], f->h);
994 } else {
995 ZVAL_STR_COPY(&args[0], f->key);
996 }
997 if (s->key == NULL) {
998 ZVAL_LONG(&args[1], s->h);
999 } else {
1000 ZVAL_STR_COPY(&args[1], s->key);
1001 }
1002
1003 BG(user_compare_fci).param_count = 2;
1004 BG(user_compare_fci).params = args;
1005 BG(user_compare_fci).retval = &retval;
1006 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
1007 zval_ptr_dtor(&args[1]);
1008 zval_ptr_dtor(&args[0]);
1009 if (UNEXPECTED(call_failed)) {
1010 return 0;
1011 }
1012
1013 if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
1014 if (!ARRAYG(compare_deprecation_thrown)) {
1015 php_error_docref(NULL, E_DEPRECATED,
1016 "Returning bool from comparison function is deprecated, "
1017 "return an integer less than, equal to, or greater than zero");
1018 ARRAYG(compare_deprecation_thrown) = 1;
1019 }
1020
1021 if (Z_TYPE(retval) == IS_FALSE) {
1022 /* Retry with swapped operands. */
1023 if (s->key == NULL) {
1024 ZVAL_LONG(&args[0], s->h);
1025 } else {
1026 ZVAL_STR_COPY(&args[0], s->key);
1027 }
1028 if (f->key == NULL) {
1029 ZVAL_LONG(&args[1], f->h);
1030 } else {
1031 ZVAL_STR_COPY(&args[1], f->key);
1032 }
1033
1034 call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
1035 zval_ptr_dtor(&args[1]);
1036 zval_ptr_dtor(&args[0]);
1037 if (call_failed) {
1038 return 0;
1039 }
1040
1041 zend_long ret = zval_get_long(&retval);
1042 zval_ptr_dtor(&retval);
1043 return -ZEND_NORMALIZE_BOOL(ret);
1044 }
1045 }
1046
1047 zend_long result = zval_get_long(&retval);
1048 zval_ptr_dtor(&retval);
1049 return ZEND_NORMALIZE_BOOL(result);
1050 }
1051 /* }}} */
1052
php_array_user_key_compare(Bucket * a,Bucket * b)1053 static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */
1054 {
1055 RETURN_STABLE_SORT(a, b, php_array_user_key_compare_unstable(a, b));
1056 }
1057 /* }}} */
1058
1059 /* {{{ Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)1060 PHP_FUNCTION(uksort)
1061 {
1062 php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1063 }
1064 /* }}} */
1065
get_ht_for_iap(zval * zv,bool separate)1066 static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
1067 if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
1068 return Z_ARRVAL_P(zv);
1069 }
1070
1071 ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
1072 php_error_docref(NULL, E_DEPRECATED,
1073 "Calling %s() on an object is deprecated", get_active_function_name());
1074
1075 zend_object *zobj = Z_OBJ_P(zv);
1076 if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
1077 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
1078 GC_DELREF(zobj->properties);
1079 }
1080 zobj->properties = zend_array_dup(zobj->properties);
1081 }
1082 return zobj->handlers->get_properties(zobj);
1083 }
1084
1085 /* {{{ Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)1086 PHP_FUNCTION(end)
1087 {
1088 zval *array_zv;
1089 zval *entry;
1090
1091 ZEND_PARSE_PARAMETERS_START(1, 1)
1092 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1093 ZEND_PARSE_PARAMETERS_END();
1094
1095 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1096 if (zend_hash_num_elements(array) == 0) {
1097 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1098 RETURN_FALSE;
1099 }
1100 zend_hash_internal_pointer_end(array);
1101
1102 if (USED_RET()) {
1103 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1104 RETURN_FALSE;
1105 }
1106
1107 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1108 entry = Z_INDIRECT_P(entry);
1109 }
1110
1111 RETURN_COPY_DEREF(entry);
1112 }
1113 }
1114 /* }}} */
1115
1116 /* {{{ Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)1117 PHP_FUNCTION(prev)
1118 {
1119 zval *array_zv;
1120 zval *entry;
1121
1122 ZEND_PARSE_PARAMETERS_START(1, 1)
1123 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1124 ZEND_PARSE_PARAMETERS_END();
1125
1126 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1127 if (zend_hash_num_elements(array) == 0) {
1128 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1129 RETURN_FALSE;
1130 }
1131 zend_hash_move_backwards(array);
1132
1133 if (USED_RET()) {
1134 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1135 RETURN_FALSE;
1136 }
1137
1138 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1139 entry = Z_INDIRECT_P(entry);
1140 }
1141
1142 RETURN_COPY_DEREF(entry);
1143 }
1144 }
1145 /* }}} */
1146
1147 /* {{{ Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)1148 PHP_FUNCTION(next)
1149 {
1150 zval *array_zv;
1151 zval *entry;
1152
1153 ZEND_PARSE_PARAMETERS_START(1, 1)
1154 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1155 ZEND_PARSE_PARAMETERS_END();
1156
1157 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1158 if (zend_hash_num_elements(array) == 0) {
1159 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1160 RETURN_FALSE;
1161 }
1162 zend_hash_move_forward(array);
1163
1164 if (USED_RET()) {
1165 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1166 RETURN_FALSE;
1167 }
1168
1169 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1170 entry = Z_INDIRECT_P(entry);
1171 }
1172
1173 RETURN_COPY_DEREF(entry);
1174 }
1175 }
1176 /* }}} */
1177
1178 /* {{{ Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)1179 PHP_FUNCTION(reset)
1180 {
1181 zval *array_zv;
1182 zval *entry;
1183
1184 ZEND_PARSE_PARAMETERS_START(1, 1)
1185 Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1186 ZEND_PARSE_PARAMETERS_END();
1187
1188 HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1189 if (zend_hash_num_elements(array) == 0) {
1190 /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1191 RETURN_FALSE;
1192 }
1193 zend_hash_internal_pointer_reset(array);
1194
1195 if (USED_RET()) {
1196 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1197 RETURN_FALSE;
1198 }
1199
1200 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1201 entry = Z_INDIRECT_P(entry);
1202 }
1203
1204 RETURN_COPY_DEREF(entry);
1205 }
1206 }
1207 /* }}} */
1208
1209 /* {{{ Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)1210 PHP_FUNCTION(current)
1211 {
1212 zval *array_zv;
1213 zval *entry;
1214
1215 ZEND_PARSE_PARAMETERS_START(1, 1)
1216 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1217 ZEND_PARSE_PARAMETERS_END();
1218
1219 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1220 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1221 RETURN_FALSE;
1222 }
1223
1224 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1225 entry = Z_INDIRECT_P(entry);
1226 }
1227
1228 RETURN_COPY_DEREF(entry);
1229 }
1230 /* }}} */
1231
1232 /* {{{ Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)1233 PHP_FUNCTION(key)
1234 {
1235 zval *array_zv;
1236
1237 ZEND_PARSE_PARAMETERS_START(1, 1)
1238 Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1239 ZEND_PARSE_PARAMETERS_END();
1240
1241 HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1242 zend_hash_get_current_key_zval(array, return_value);
1243 }
1244 /* }}} */
1245
1246 /* {{{
1247 * proto mixed min(array values)
1248 * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1249 Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)1250 PHP_FUNCTION(min)
1251 {
1252 int argc;
1253 zval *args = NULL;
1254
1255 ZEND_PARSE_PARAMETERS_START(1, -1)
1256 Z_PARAM_VARIADIC('+', args, argc)
1257 ZEND_PARSE_PARAMETERS_END();
1258
1259 /* mixed min ( array $values ) */
1260 if (argc == 1) {
1261 if (Z_TYPE(args[0]) != IS_ARRAY) {
1262 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
1263 RETURN_THROWS();
1264 } else {
1265 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare_unstable, 0);
1266 if (result) {
1267 RETURN_COPY_DEREF(result);
1268 } else {
1269 zend_argument_value_error(1, "must contain at least one element");
1270 RETURN_THROWS();
1271 }
1272 }
1273 } else {
1274 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1275 zval *min, result;
1276 int i;
1277
1278 min = &args[0];
1279
1280 for (i = 1; i < argc; i++) {
1281 is_smaller_function(&result, &args[i], min);
1282 if (Z_TYPE(result) == IS_TRUE) {
1283 min = &args[i];
1284 }
1285 }
1286
1287 RETURN_COPY(min);
1288 }
1289 }
1290 /* }}} */
1291
1292 /* {{{
1293 * proto mixed max(array values)
1294 * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1295 Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)1296 PHP_FUNCTION(max)
1297 {
1298 zval *args = NULL;
1299 int argc;
1300
1301 ZEND_PARSE_PARAMETERS_START(1, -1)
1302 Z_PARAM_VARIADIC('+', args, argc)
1303 ZEND_PARSE_PARAMETERS_END();
1304
1305 /* mixed max ( array $values ) */
1306 if (argc == 1) {
1307 if (Z_TYPE(args[0]) != IS_ARRAY) {
1308 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
1309 RETURN_THROWS();
1310 } else {
1311 zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare_unstable, 1);
1312 if (result) {
1313 RETURN_COPY_DEREF(result);
1314 } else {
1315 zend_argument_value_error(1, "must contain at least one element");
1316 RETURN_THROWS();
1317 }
1318 }
1319 } else {
1320 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1321 zval *max, result;
1322 int i;
1323
1324 max = &args[0];
1325
1326 for (i = 1; i < argc; i++) {
1327 is_smaller_or_equal_function(&result, &args[i], max);
1328 if (Z_TYPE(result) == IS_FALSE) {
1329 max = &args[i];
1330 }
1331 }
1332
1333 RETURN_COPY(max);
1334 }
1335 }
1336 /* }}} */
1337
1338 typedef struct {
1339 zend_fcall_info fci;
1340 zend_fcall_info_cache fci_cache;
1341 } php_array_walk_context;
1342
php_array_walk(php_array_walk_context * context,zval * array,zval * userdata,int recursive)1343 static int php_array_walk(
1344 php_array_walk_context *context, zval *array, zval *userdata, int recursive)
1345 {
1346 zval args[3], /* Arguments to userland function */
1347 retval, /* Return value - unused */
1348 *zv;
1349 HashTable *target_hash = HASH_OF(array);
1350 HashPosition pos;
1351 uint32_t ht_iter;
1352 int result = SUCCESS;
1353
1354 /* Create a local copy of fci, as we want to use different arguments at different
1355 * levels of recursion. */
1356 zend_fcall_info fci = context->fci;
1357
1358 if (zend_hash_num_elements(target_hash) == 0) {
1359 return result;
1360 }
1361
1362 /* Set up known arguments */
1363 ZVAL_UNDEF(&args[1]);
1364 if (userdata) {
1365 ZVAL_COPY(&args[2], userdata);
1366 }
1367
1368 fci.retval = &retval;
1369 fci.param_count = userdata ? 3 : 2;
1370 fci.params = args;
1371
1372 zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1373 ht_iter = zend_hash_iterator_add(target_hash, pos);
1374
1375 /* Iterate through hash */
1376 do {
1377 /* Retrieve value */
1378 zv = zend_hash_get_current_data_ex(target_hash, &pos);
1379 if (zv == NULL) {
1380 break;
1381 }
1382
1383 /* Skip undefined indirect elements */
1384 if (Z_TYPE_P(zv) == IS_INDIRECT) {
1385 zv = Z_INDIRECT_P(zv);
1386 if (Z_TYPE_P(zv) == IS_UNDEF) {
1387 zend_hash_move_forward_ex(target_hash, &pos);
1388 continue;
1389 }
1390
1391 /* Add type source for property references. */
1392 if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1393 zend_property_info *prop_info =
1394 zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1395 if (prop_info) {
1396 ZVAL_NEW_REF(zv, zv);
1397 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1398 }
1399 }
1400 }
1401
1402 /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1403 ZVAL_MAKE_REF(zv);
1404
1405 /* Retrieve key */
1406 zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1407
1408 /* Move to next element already now -- this mirrors the approach used by foreach
1409 * and ensures proper behavior with regard to modifications. */
1410 zend_hash_move_forward_ex(target_hash, &pos);
1411
1412 /* Back up hash position, as it may change */
1413 EG(ht_iterators)[ht_iter].pos = pos;
1414
1415 if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1416 HashTable *thash;
1417 zval ref;
1418 ZVAL_COPY_VALUE(&ref, zv);
1419
1420 ZVAL_DEREF(zv);
1421 SEPARATE_ARRAY(zv);
1422 thash = Z_ARRVAL_P(zv);
1423 if (GC_IS_RECURSIVE(thash)) {
1424 zend_throw_error(NULL, "Recursion detected");
1425 result = FAILURE;
1426 break;
1427 }
1428
1429 Z_ADDREF(ref);
1430 GC_PROTECT_RECURSION(thash);
1431 result = php_array_walk(context, zv, userdata, recursive);
1432 if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1433 /* If the hashtable changed in the meantime, we'll "leak" this apply count
1434 * increment -- our reference to thash is no longer valid. */
1435 GC_UNPROTECT_RECURSION(thash);
1436 }
1437 zval_ptr_dtor(&ref);
1438 } else {
1439 ZVAL_COPY(&args[0], zv);
1440
1441 /* Call the userland function */
1442 result = zend_call_function(&fci, &context->fci_cache);
1443 if (result == SUCCESS) {
1444 zval_ptr_dtor(&retval);
1445 }
1446
1447 zval_ptr_dtor(&args[0]);
1448 }
1449
1450 if (Z_TYPE(args[1]) != IS_UNDEF) {
1451 zval_ptr_dtor(&args[1]);
1452 ZVAL_UNDEF(&args[1]);
1453 }
1454
1455 if (result == FAILURE) {
1456 break;
1457 }
1458
1459 /* Reload array and position -- both may have changed */
1460 if (Z_TYPE_P(array) == IS_ARRAY) {
1461 pos = zend_hash_iterator_pos_ex(ht_iter, array);
1462 target_hash = Z_ARRVAL_P(array);
1463 } else if (Z_TYPE_P(array) == IS_OBJECT) {
1464 target_hash = Z_OBJPROP_P(array);
1465 pos = zend_hash_iterator_pos(ht_iter, target_hash);
1466 } else {
1467 zend_type_error("Iterated value is no longer an array or object");
1468 result = FAILURE;
1469 break;
1470 }
1471 } while (!EG(exception));
1472
1473 if (userdata) {
1474 zval_ptr_dtor(&args[2]);
1475 }
1476 zend_hash_iterator_del(ht_iter);
1477 return result;
1478 }
1479
1480 /* {{{ Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1481 PHP_FUNCTION(array_walk)
1482 {
1483 zval *array;
1484 zval *userdata = NULL;
1485 php_array_walk_context context;
1486
1487 ZEND_PARSE_PARAMETERS_START(2, 3)
1488 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1489 Z_PARAM_FUNC(context.fci, context.fci_cache)
1490 Z_PARAM_OPTIONAL
1491 Z_PARAM_ZVAL(userdata)
1492 ZEND_PARSE_PARAMETERS_END();
1493
1494 php_array_walk(&context, array, userdata, 0);
1495 RETURN_TRUE;
1496 }
1497 /* }}} */
1498
1499 /* {{{ Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1500 PHP_FUNCTION(array_walk_recursive)
1501 {
1502 zval *array;
1503 zval *userdata = NULL;
1504 php_array_walk_context context;
1505
1506 ZEND_PARSE_PARAMETERS_START(2, 3)
1507 Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1508 Z_PARAM_FUNC(context.fci, context.fci_cache)
1509 Z_PARAM_OPTIONAL
1510 Z_PARAM_ZVAL(userdata)
1511 ZEND_PARSE_PARAMETERS_END();
1512
1513 php_array_walk(&context, array, userdata, 1);
1514 RETURN_TRUE;
1515 }
1516 /* }}} */
1517
1518 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1519 * 0 = return boolean
1520 * 1 = return key
1521 */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1522 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1523 {
1524 zval *value, /* value to check for */
1525 *array, /* array to check in */
1526 *entry; /* pointer to array entry */
1527 zend_ulong num_idx;
1528 zend_string *str_idx;
1529 bool strict = 0; /* strict comparison or not */
1530
1531 ZEND_PARSE_PARAMETERS_START(2, 3)
1532 Z_PARAM_ZVAL(value)
1533 Z_PARAM_ARRAY(array)
1534 Z_PARAM_OPTIONAL
1535 Z_PARAM_BOOL(strict)
1536 ZEND_PARSE_PARAMETERS_END();
1537
1538 if (strict) {
1539 if (Z_TYPE_P(value) == IS_LONG) {
1540 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1541 ZVAL_DEREF(entry);
1542 if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1543 if (behavior == 0) {
1544 RETURN_TRUE;
1545 } else {
1546 if (str_idx) {
1547 RETURN_STR_COPY(str_idx);
1548 } else {
1549 RETURN_LONG(num_idx);
1550 }
1551 }
1552 }
1553 } ZEND_HASH_FOREACH_END();
1554 } else {
1555 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1556 ZVAL_DEREF(entry);
1557 if (fast_is_identical_function(value, entry)) {
1558 if (behavior == 0) {
1559 RETURN_TRUE;
1560 } else {
1561 if (str_idx) {
1562 RETURN_STR_COPY(str_idx);
1563 } else {
1564 RETURN_LONG(num_idx);
1565 }
1566 }
1567 }
1568 } ZEND_HASH_FOREACH_END();
1569 }
1570 } else {
1571 if (Z_TYPE_P(value) == IS_LONG) {
1572 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1573 if (fast_equal_check_long(value, entry)) {
1574 if (behavior == 0) {
1575 RETURN_TRUE;
1576 } else {
1577 if (str_idx) {
1578 RETURN_STR_COPY(str_idx);
1579 } else {
1580 RETURN_LONG(num_idx);
1581 }
1582 }
1583 }
1584 } ZEND_HASH_FOREACH_END();
1585 } else if (Z_TYPE_P(value) == IS_STRING) {
1586 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1587 if (fast_equal_check_string(value, entry)) {
1588 if (behavior == 0) {
1589 RETURN_TRUE;
1590 } else {
1591 if (str_idx) {
1592 RETURN_STR_COPY(str_idx);
1593 } else {
1594 RETURN_LONG(num_idx);
1595 }
1596 }
1597 }
1598 } ZEND_HASH_FOREACH_END();
1599 } else {
1600 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1601 if (fast_equal_check_function(value, entry)) {
1602 if (behavior == 0) {
1603 RETURN_TRUE;
1604 } else {
1605 if (str_idx) {
1606 RETURN_STR_COPY(str_idx);
1607 } else {
1608 RETURN_LONG(num_idx);
1609 }
1610 }
1611 }
1612 } ZEND_HASH_FOREACH_END();
1613 }
1614 }
1615
1616 RETURN_FALSE;
1617 }
1618 /* }}} */
1619
1620 /* {{{ Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1621 PHP_FUNCTION(in_array)
1622 {
1623 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1624 }
1625 /* }}} */
1626
1627 /* {{{ Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1628 PHP_FUNCTION(array_search)
1629 {
1630 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1631 }
1632 /* }}} */
1633
php_valid_var_name(const char * var_name,size_t var_name_len)1634 static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
1635 {
1636 #if 1
1637 /* first 256 bits for first character, and second 256 bits for the next */
1638 static const uint32_t charset[8] = {
1639 /* 31 0 63 32 95 64 127 96 */
1640 0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1641 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1642 static const uint32_t charset2[8] = {
1643 /* 31 0 63 32 95 64 127 96 */
1644 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1645 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1646 #endif
1647 size_t i;
1648 uint32_t ch;
1649
1650 if (UNEXPECTED(!var_name_len)) {
1651 return 0;
1652 }
1653
1654 /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1655 ch = (uint32_t)((unsigned char *)var_name)[0];
1656 #if 1
1657 if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1658 #else
1659 if (var_name[0] != '_' &&
1660 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1661 (ch < 97 /* a */ || /* z */ ch > 122) &&
1662 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1663 ) {
1664 #endif
1665 return 0;
1666 }
1667
1668 /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1669 if (var_name_len > 1) {
1670 i = 1;
1671 do {
1672 ch = (uint32_t)((unsigned char *)var_name)[i];
1673 #if 1
1674 if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1675 #else
1676 if (var_name[i] != '_' &&
1677 (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
1678 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1679 (ch < 97 /* a */ || /* z */ ch > 122) &&
1680 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1681 ) {
1682 #endif
1683 return 0;
1684 }
1685 } while (++i < var_name_len);
1686 }
1687 return 1;
1688 }
1689 /* }}} */
1690
1691 PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore) /* {{{ */
1692 {
1693 ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1694 memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
1695
1696 if (add_underscore) {
1697 Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
1698 }
1699
1700 memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1701
1702 return SUCCESS;
1703 }
1704 /* }}} */
1705
1706 static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1707 {
1708 zend_long count = 0;
1709 zend_string *var_name;
1710 zval *entry, *orig_var;
1711
1712 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1713 if (!var_name) {
1714 continue;
1715 }
1716 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1717 if (orig_var) {
1718 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1719 orig_var = Z_INDIRECT_P(orig_var);
1720 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1721 continue;
1722 }
1723 }
1724 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1725 continue;
1726 }
1727 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1728 continue;
1729 }
1730 if (zend_string_equals_literal(var_name, "this")) {
1731 zend_throw_error(NULL, "Cannot re-assign $this");
1732 return -1;
1733 }
1734 if (Z_ISREF_P(entry)) {
1735 Z_ADDREF_P(entry);
1736 } else {
1737 ZVAL_MAKE_REF_EX(entry, 2);
1738 }
1739 zval_ptr_dtor(orig_var);
1740 ZVAL_REF(orig_var, Z_REF_P(entry));
1741 count++;
1742 }
1743 } ZEND_HASH_FOREACH_END();
1744
1745 return count;
1746 }
1747 /* }}} */
1748
1749 static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
1750 {
1751 zend_long count = 0;
1752 zend_string *var_name;
1753 zval *entry, *orig_var;
1754
1755 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1756 if (!var_name) {
1757 continue;
1758 }
1759 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1760 if (orig_var) {
1761 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1762 orig_var = Z_INDIRECT_P(orig_var);
1763 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1764 continue;
1765 }
1766 }
1767 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1768 continue;
1769 }
1770 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1771 continue;
1772 }
1773 if (zend_string_equals_literal(var_name, "this")) {
1774 zend_throw_error(NULL, "Cannot re-assign $this");
1775 return -1;
1776 }
1777 ZVAL_DEREF(entry);
1778 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1779 if (UNEXPECTED(EG(exception))) {
1780 return -1;
1781 }
1782 count++;
1783 }
1784 } ZEND_HASH_FOREACH_END();
1785
1786 return count;
1787 }
1788 /* }}} */
1789
1790 static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1791 {
1792 zend_long count = 0;
1793 zend_string *var_name;
1794 zval *entry, *orig_var;
1795
1796 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1797 if (!var_name) {
1798 continue;
1799 }
1800 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1801 continue;
1802 }
1803 if (zend_string_equals_literal(var_name, "this")) {
1804 zend_throw_error(NULL, "Cannot re-assign $this");
1805 return -1;
1806 }
1807 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1808 if (orig_var) {
1809 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1810 orig_var = Z_INDIRECT_P(orig_var);
1811 }
1812 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1813 continue;
1814 }
1815 if (Z_ISREF_P(entry)) {
1816 Z_ADDREF_P(entry);
1817 } else {
1818 ZVAL_MAKE_REF_EX(entry, 2);
1819 }
1820 zval_ptr_dtor(orig_var);
1821 ZVAL_REF(orig_var, Z_REF_P(entry));
1822 } else {
1823 if (Z_ISREF_P(entry)) {
1824 Z_ADDREF_P(entry);
1825 } else {
1826 ZVAL_MAKE_REF_EX(entry, 2);
1827 }
1828 zend_hash_add_new(symbol_table, var_name, entry);
1829 }
1830 count++;
1831 } ZEND_HASH_FOREACH_END();
1832
1833 return count;
1834 }
1835 /* }}} */
1836
1837 static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
1838 {
1839 zend_long count = 0;
1840 zend_string *var_name;
1841 zval *entry, *orig_var;
1842
1843 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1844 if (!var_name) {
1845 continue;
1846 }
1847 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1848 continue;
1849 }
1850 if (zend_string_equals_literal(var_name, "this")) {
1851 zend_throw_error(NULL, "Cannot re-assign $this");
1852 return -1;
1853 }
1854 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1855 if (orig_var) {
1856 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1857 orig_var = Z_INDIRECT_P(orig_var);
1858 }
1859 if (zend_string_equals_literal(var_name, "GLOBALS")) {
1860 continue;
1861 }
1862 ZVAL_DEREF(entry);
1863 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1864 if (UNEXPECTED(EG(exception))) {
1865 return -1;
1866 }
1867 } else {
1868 ZVAL_DEREF(entry);
1869 Z_TRY_ADDREF_P(entry);
1870 zend_hash_add_new(symbol_table, var_name, entry);
1871 }
1872 count++;
1873 } ZEND_HASH_FOREACH_END();
1874
1875 return count;
1876 }
1877 /* }}} */
1878
1879 static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1880 {
1881 zend_long count = 0;
1882 zend_string *var_name;
1883 zval *entry, *orig_var, final_name;
1884
1885 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1886 if (!var_name) {
1887 continue;
1888 }
1889 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1890 if (orig_var) {
1891 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1892 orig_var = Z_INDIRECT_P(orig_var);
1893 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1894 if (Z_ISREF_P(entry)) {
1895 Z_ADDREF_P(entry);
1896 } else {
1897 ZVAL_MAKE_REF_EX(entry, 2);
1898 }
1899 ZVAL_REF(orig_var, Z_REF_P(entry));
1900 count++;
1901 continue;
1902 }
1903 }
1904 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1905 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1906 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
1907 zend_throw_error(NULL, "Cannot re-assign $this");
1908 return -1;
1909 } else {
1910 if (Z_ISREF_P(entry)) {
1911 Z_ADDREF_P(entry);
1912 } else {
1913 ZVAL_MAKE_REF_EX(entry, 2);
1914 }
1915 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1916 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1917 orig_var = Z_INDIRECT_P(orig_var);
1918 }
1919 zval_ptr_dtor(orig_var);
1920 ZVAL_REF(orig_var, Z_REF_P(entry));
1921 } else {
1922 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1923 }
1924 count++;
1925 }
1926 }
1927 zval_ptr_dtor_str(&final_name);
1928 }
1929 } ZEND_HASH_FOREACH_END();
1930
1931 return count;
1932 }
1933 /* }}} */
1934
1935 static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1936 {
1937 zend_long count = 0;
1938 zend_string *var_name;
1939 zval *entry, *orig_var, final_name;
1940
1941 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1942 if (!var_name) {
1943 continue;
1944 }
1945 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1946 if (orig_var) {
1947 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1948 orig_var = Z_INDIRECT_P(orig_var);
1949 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1950 ZVAL_COPY_DEREF(orig_var, entry);
1951 count++;
1952 continue;
1953 }
1954 }
1955 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1956 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1957 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
1958 zend_throw_error(NULL, "Cannot re-assign $this");
1959 return -1;
1960 } else {
1961 ZVAL_DEREF(entry);
1962 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1963 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1964 orig_var = Z_INDIRECT_P(orig_var);
1965 }
1966 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1967 if (UNEXPECTED(EG(exception))) {
1968 zend_string_release_ex(Z_STR(final_name), 0);
1969 return -1;
1970 }
1971 } else {
1972 Z_TRY_ADDREF_P(entry);
1973 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
1974 }
1975 count++;
1976 }
1977 }
1978 zval_ptr_dtor_str(&final_name);
1979 }
1980 } ZEND_HASH_FOREACH_END();
1981
1982 return count;
1983 }
1984 /* }}} */
1985
1986 static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
1987 {
1988 zend_long count = 0;
1989 zend_string *var_name;
1990 zval *entry, *orig_var, final_name;
1991
1992 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1993 if (!var_name) {
1994 continue;
1995 }
1996 if (ZSTR_LEN(var_name) == 0) {
1997 continue;
1998 }
1999 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2000 if (orig_var) {
2001 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2002 orig_var = Z_INDIRECT_P(orig_var);
2003 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2004 if (Z_ISREF_P(entry)) {
2005 Z_ADDREF_P(entry);
2006 } else {
2007 ZVAL_MAKE_REF_EX(entry, 2);
2008 }
2009 ZVAL_REF(orig_var, Z_REF_P(entry));
2010 count++;
2011 continue;
2012 }
2013 }
2014 prefix:
2015 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2016 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2017 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2018 zend_throw_error(NULL, "Cannot re-assign $this");
2019 return -1;
2020 } else {
2021 if (Z_ISREF_P(entry)) {
2022 Z_ADDREF_P(entry);
2023 } else {
2024 ZVAL_MAKE_REF_EX(entry, 2);
2025 }
2026 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2027 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2028 orig_var = Z_INDIRECT_P(orig_var);
2029 }
2030 zval_ptr_dtor(orig_var);
2031 ZVAL_REF(orig_var, Z_REF_P(entry));
2032 } else {
2033 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2034 }
2035 count++;
2036 }
2037 }
2038 zval_ptr_dtor_str(&final_name);
2039 } else {
2040 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2041 continue;
2042 }
2043 if (zend_string_equals_literal(var_name, "this")) {
2044 goto prefix;
2045 }
2046 if (Z_ISREF_P(entry)) {
2047 Z_ADDREF_P(entry);
2048 } else {
2049 ZVAL_MAKE_REF_EX(entry, 2);
2050 }
2051 zend_hash_add_new(symbol_table, var_name, entry);
2052 count++;
2053 }
2054 } ZEND_HASH_FOREACH_END();
2055
2056 return count;
2057 }
2058 /* }}} */
2059
2060 static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2061 {
2062 zend_long count = 0;
2063 zend_string *var_name;
2064 zval *entry, *orig_var, final_name;
2065
2066 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2067 if (!var_name) {
2068 continue;
2069 }
2070 if (ZSTR_LEN(var_name) == 0) {
2071 continue;
2072 }
2073 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2074 if (orig_var) {
2075 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2076 orig_var = Z_INDIRECT_P(orig_var);
2077 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2078 ZVAL_COPY_DEREF(orig_var, entry);
2079 count++;
2080 continue;
2081 }
2082 }
2083 prefix:
2084 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2085 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2086 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2087 zend_throw_error(NULL, "Cannot re-assign $this");
2088 return -1;
2089 } else {
2090 ZVAL_DEREF(entry);
2091 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2092 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2093 orig_var = Z_INDIRECT_P(orig_var);
2094 }
2095 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2096 if (UNEXPECTED(EG(exception))) {
2097 zend_string_release_ex(Z_STR(final_name), 0);
2098 return -1;
2099 }
2100 } else {
2101 Z_TRY_ADDREF_P(entry);
2102 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2103 }
2104 count++;
2105 }
2106 }
2107 zval_ptr_dtor_str(&final_name);
2108 } else {
2109 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2110 continue;
2111 }
2112 if (zend_string_equals_literal(var_name, "this")) {
2113 goto prefix;
2114 }
2115 ZVAL_DEREF(entry);
2116 Z_TRY_ADDREF_P(entry);
2117 zend_hash_add_new(symbol_table, var_name, entry);
2118 count++;
2119 }
2120 } ZEND_HASH_FOREACH_END();
2121
2122 return count;
2123 }
2124 /* }}} */
2125
2126 static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2127 {
2128 zend_long count = 0;
2129 zend_string *var_name;
2130 zend_ulong num_key;
2131 zval *entry, *orig_var, final_name;
2132
2133 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2134 if (var_name) {
2135 if (ZSTR_LEN(var_name) == 0) {
2136 continue;
2137 }
2138 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2139 } else {
2140 zend_string *str = zend_long_to_str(num_key);
2141 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2142 zend_string_release_ex(str, 0);
2143 }
2144 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2145 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2146 zend_throw_error(NULL, "Cannot re-assign $this");
2147 return -1;
2148 } else {
2149 if (Z_ISREF_P(entry)) {
2150 Z_ADDREF_P(entry);
2151 } else {
2152 ZVAL_MAKE_REF_EX(entry, 2);
2153 }
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 zval_ptr_dtor(orig_var);
2159 ZVAL_REF(orig_var, Z_REF_P(entry));
2160 } else {
2161 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2162 }
2163 count++;
2164 }
2165 }
2166 zval_ptr_dtor_str(&final_name);
2167 } ZEND_HASH_FOREACH_END();
2168
2169 return count;
2170 }
2171 /* }}} */
2172
2173 static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2174 {
2175 zend_long count = 0;
2176 zend_string *var_name;
2177 zend_ulong num_key;
2178 zval *entry, *orig_var, final_name;
2179
2180 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2181 if (var_name) {
2182 if (ZSTR_LEN(var_name) == 0) {
2183 continue;
2184 }
2185 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2186 } else {
2187 zend_string *str = zend_long_to_str(num_key);
2188 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2189 zend_string_release_ex(str, 0);
2190 }
2191 if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2192 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2193 zend_throw_error(NULL, "Cannot re-assign $this");
2194 return -1;
2195 } else {
2196 ZVAL_DEREF(entry);
2197 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2198 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2199 orig_var = Z_INDIRECT_P(orig_var);
2200 }
2201 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2202 if (UNEXPECTED(EG(exception))) {
2203 zend_string_release_ex(Z_STR(final_name), 0);
2204 return -1;
2205 }
2206 } else {
2207 Z_TRY_ADDREF_P(entry);
2208 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2209 }
2210 count++;
2211 }
2212 }
2213 zval_ptr_dtor_str(&final_name);
2214 } ZEND_HASH_FOREACH_END();
2215
2216 return count;
2217 }
2218 /* }}} */
2219
2220 static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2221 {
2222 zend_long count = 0;
2223 zend_string *var_name;
2224 zend_ulong num_key;
2225 zval *entry, *orig_var, final_name;
2226
2227 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2228 if (var_name) {
2229 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2230 || zend_string_equals_literal(var_name, "this")) {
2231 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2232 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2233 zval_ptr_dtor_str(&final_name);
2234 continue;
2235 }
2236 } else {
2237 ZVAL_STR_COPY(&final_name, var_name);
2238 }
2239 } else {
2240 zend_string *str = zend_long_to_str(num_key);
2241 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2242 zend_string_release_ex(str, 0);
2243 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2244 zval_ptr_dtor_str(&final_name);
2245 continue;
2246 }
2247 }
2248 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2249 zend_throw_error(NULL, "Cannot re-assign $this");
2250 return -1;
2251 } else {
2252 if (Z_ISREF_P(entry)) {
2253 Z_ADDREF_P(entry);
2254 } else {
2255 ZVAL_MAKE_REF_EX(entry, 2);
2256 }
2257 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2258 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2259 orig_var = Z_INDIRECT_P(orig_var);
2260 }
2261 zval_ptr_dtor(orig_var);
2262 ZVAL_REF(orig_var, Z_REF_P(entry));
2263 } else {
2264 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2265 }
2266 count++;
2267 }
2268 zval_ptr_dtor_str(&final_name);
2269 } ZEND_HASH_FOREACH_END();
2270
2271 return count;
2272 }
2273 /* }}} */
2274
2275 static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
2276 {
2277 zend_long count = 0;
2278 zend_string *var_name;
2279 zend_ulong num_key;
2280 zval *entry, *orig_var, final_name;
2281
2282 ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2283 if (var_name) {
2284 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
2285 || zend_string_equals_literal(var_name, "this")) {
2286 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
2287 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2288 zval_ptr_dtor_str(&final_name);
2289 continue;
2290 }
2291 } else {
2292 ZVAL_STR_COPY(&final_name, var_name);
2293 }
2294 } else {
2295 zend_string *str = zend_long_to_str(num_key);
2296 php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
2297 zend_string_release_ex(str, 0);
2298 if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
2299 zval_ptr_dtor_str(&final_name);
2300 continue;
2301 }
2302 }
2303 if (zend_string_equals_literal(Z_STR(final_name), "this")) {
2304 zend_throw_error(NULL, "Cannot re-assign $this");
2305 return -1;
2306 } else {
2307 ZVAL_DEREF(entry);
2308 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
2309 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2310 orig_var = Z_INDIRECT_P(orig_var);
2311 }
2312 ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2313 if (UNEXPECTED(EG(exception))) {
2314 zend_string_release_ex(Z_STR(final_name), 0);
2315 return -1;
2316 }
2317 } else {
2318 Z_TRY_ADDREF_P(entry);
2319 zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
2320 }
2321 count++;
2322 }
2323 zval_ptr_dtor_str(&final_name);
2324 } ZEND_HASH_FOREACH_END();
2325
2326 return count;
2327 }
2328 /* }}} */
2329
2330 static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2331 {
2332 zend_long count = 0;
2333 zend_string *var_name;
2334 zval *entry, *orig_var;
2335
2336 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2337 if (!var_name) {
2338 continue;
2339 }
2340 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2341 continue;
2342 }
2343 if (zend_string_equals_literal(var_name, "this")) {
2344 continue;
2345 }
2346 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2347 if (orig_var) {
2348 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2349 orig_var = Z_INDIRECT_P(orig_var);
2350 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2351 if (Z_ISREF_P(entry)) {
2352 Z_ADDREF_P(entry);
2353 } else {
2354 ZVAL_MAKE_REF_EX(entry, 2);
2355 }
2356 ZVAL_REF(orig_var, Z_REF_P(entry));
2357 count++;
2358 }
2359 }
2360 } else {
2361 if (Z_ISREF_P(entry)) {
2362 Z_ADDREF_P(entry);
2363 } else {
2364 ZVAL_MAKE_REF_EX(entry, 2);
2365 }
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 static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
2376 {
2377 zend_long count = 0;
2378 zend_string *var_name;
2379 zval *entry, *orig_var;
2380
2381 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2382 if (!var_name) {
2383 continue;
2384 }
2385 if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
2386 continue;
2387 }
2388 if (zend_string_equals_literal(var_name, "this")) {
2389 continue;
2390 }
2391 orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2392 if (orig_var) {
2393 if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2394 orig_var = Z_INDIRECT_P(orig_var);
2395 if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2396 ZVAL_COPY_DEREF(orig_var, entry);
2397 count++;
2398 }
2399 }
2400 } else {
2401 ZVAL_DEREF(entry);
2402 Z_TRY_ADDREF_P(entry);
2403 zend_hash_add_new(symbol_table, var_name, entry);
2404 count++;
2405 }
2406 } ZEND_HASH_FOREACH_END();
2407
2408 return count;
2409 }
2410 /* }}} */
2411
2412 /* {{{ Imports variables into symbol table from an array */
2413 PHP_FUNCTION(extract)
2414 {
2415 zval *var_array_param;
2416 zend_long extract_refs;
2417 zend_long extract_type = EXTR_OVERWRITE;
2418 zend_string *prefix = NULL;
2419 zend_long count;
2420 zend_array *symbol_table;
2421
2422 ZEND_PARSE_PARAMETERS_START(1, 3)
2423 Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2424 Z_PARAM_OPTIONAL
2425 Z_PARAM_LONG(extract_type)
2426 Z_PARAM_STR(prefix)
2427 ZEND_PARSE_PARAMETERS_END();
2428
2429 extract_refs = (extract_type & EXTR_REFS);
2430 if (extract_refs) {
2431 SEPARATE_ARRAY(var_array_param);
2432 }
2433 extract_type &= 0xff;
2434
2435 if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
2436 zend_argument_value_error(2, "must be a valid extract type");
2437 RETURN_THROWS();
2438 }
2439
2440 if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2441 zend_argument_value_error(3, "is required when using this extract type");
2442 RETURN_THROWS();
2443 }
2444
2445 if (prefix) {
2446 if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
2447 zend_argument_value_error(3, "must be a valid identifier");
2448 RETURN_THROWS();
2449 }
2450 }
2451
2452 if (zend_forbid_dynamic_call("extract()") == FAILURE) {
2453 return;
2454 }
2455
2456 symbol_table = zend_rebuild_symbol_table();
2457 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2458
2459 if (extract_refs) {
2460 switch (extract_type) {
2461 case EXTR_IF_EXISTS:
2462 count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2463 break;
2464 case EXTR_OVERWRITE:
2465 count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2466 break;
2467 case EXTR_PREFIX_IF_EXISTS:
2468 count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2469 break;
2470 case EXTR_PREFIX_SAME:
2471 count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2472 break;
2473 case EXTR_PREFIX_ALL:
2474 count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2475 break;
2476 case EXTR_PREFIX_INVALID:
2477 count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2478 break;
2479 default:
2480 count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2481 break;
2482 }
2483 } else {
2484 /* The array might be stored in a local variable that will be overwritten */
2485 zval array_copy;
2486 ZVAL_COPY(&array_copy, var_array_param);
2487 switch (extract_type) {
2488 case EXTR_IF_EXISTS:
2489 count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2490 break;
2491 case EXTR_OVERWRITE:
2492 count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2493 break;
2494 case EXTR_PREFIX_IF_EXISTS:
2495 count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2496 break;
2497 case EXTR_PREFIX_SAME:
2498 count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2499 break;
2500 case EXTR_PREFIX_ALL:
2501 count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2502 break;
2503 case EXTR_PREFIX_INVALID:
2504 count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2505 break;
2506 default:
2507 count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2508 break;
2509 }
2510 zval_ptr_dtor(&array_copy);
2511 }
2512
2513 RETURN_LONG(count);
2514 }
2515 /* }}} */
2516
2517 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2518 {
2519 zval *value_ptr, data;
2520
2521 ZVAL_DEREF(entry);
2522 if (Z_TYPE_P(entry) == IS_STRING) {
2523 if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2524 ZVAL_DEREF(value_ptr);
2525 Z_TRY_ADDREF_P(value_ptr);
2526 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2527 } else if (zend_string_equals_literal(Z_STR_P(entry), "this")) {
2528 zend_object *object = zend_get_this_object(EG(current_execute_data));
2529 if (object) {
2530 ZVAL_OBJ_COPY(&data, object);
2531 zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2532 }
2533 } else {
2534 php_error_docref(NULL, E_WARNING, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
2535 }
2536 } else if (Z_TYPE_P(entry) == IS_ARRAY) {
2537 if (Z_REFCOUNTED_P(entry)) {
2538 if (Z_IS_RECURSIVE_P(entry)) {
2539 zend_throw_error(NULL, "Recursion detected");
2540 return;
2541 }
2542 Z_PROTECT_RECURSION_P(entry);
2543 }
2544 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
2545 php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
2546 } ZEND_HASH_FOREACH_END();
2547 if (Z_REFCOUNTED_P(entry)) {
2548 Z_UNPROTECT_RECURSION_P(entry);
2549 }
2550 } else {
2551 php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_type_name(entry));
2552 return;
2553 }
2554 }
2555 /* }}} */
2556
2557 /* {{{ Creates a hash containing variables and their values */
2558 PHP_FUNCTION(compact)
2559 {
2560 zval *args = NULL; /* function arguments array */
2561 uint32_t num_args, i;
2562 zend_array *symbol_table;
2563
2564 ZEND_PARSE_PARAMETERS_START(1, -1)
2565 Z_PARAM_VARIADIC('+', args, num_args)
2566 ZEND_PARSE_PARAMETERS_END();
2567
2568 if (zend_forbid_dynamic_call("compact()") == FAILURE) {
2569 return;
2570 }
2571
2572 symbol_table = zend_rebuild_symbol_table();
2573 ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2574
2575 /* compact() is probably most used with a single array of var_names
2576 or multiple string names, rather than a combination of both.
2577 So quickly guess a minimum result size based on that */
2578 if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2579 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2580 } else {
2581 array_init_size(return_value, num_args);
2582 }
2583
2584 for (i = 0; i < num_args; i++) {
2585 php_compact_var(symbol_table, return_value, &args[i], i + 1);
2586 }
2587 }
2588 /* }}} */
2589
2590 /* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2591 PHP_FUNCTION(array_fill)
2592 {
2593 zval *val;
2594 zend_long start_key, num;
2595
2596 ZEND_PARSE_PARAMETERS_START(3, 3)
2597 Z_PARAM_LONG(start_key)
2598 Z_PARAM_LONG(num)
2599 Z_PARAM_ZVAL(val)
2600 ZEND_PARSE_PARAMETERS_END();
2601
2602 if (EXPECTED(num > 0)) {
2603 if (sizeof(num) > 4 && UNEXPECTED(EXPECTED(num > 0x7fffffff))) {
2604 zend_argument_value_error(2, "is too large");
2605 RETURN_THROWS();
2606 } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2607 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2608 RETURN_THROWS();
2609 } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2610 /* create packed array */
2611 Bucket *p;
2612 zend_long n;
2613
2614 array_init_size(return_value, (uint32_t)(start_key + num));
2615 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2616 Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2617 Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2618 Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2619
2620 if (Z_REFCOUNTED_P(val)) {
2621 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2622 }
2623
2624 p = Z_ARRVAL_P(return_value)->arData;
2625 n = start_key;
2626
2627 while (start_key--) {
2628 ZVAL_UNDEF(&p->val);
2629 p++;
2630 }
2631 while (num--) {
2632 ZVAL_COPY_VALUE(&p->val, val);
2633 p->h = n++;
2634 p->key = NULL;
2635 p++;
2636 }
2637 } else {
2638 /* create hash */
2639 array_init_size(return_value, (uint32_t)num);
2640 zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2641 if (Z_REFCOUNTED_P(val)) {
2642 GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2643 }
2644 zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2645 while (--num) {
2646 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2647 start_key++;
2648 }
2649 }
2650 } else if (EXPECTED(num == 0)) {
2651 RETURN_EMPTY_ARRAY();
2652 } else {
2653 zend_argument_value_error(2, "must be greater than or equal to 0");
2654 RETURN_THROWS();
2655 }
2656 }
2657 /* }}} */
2658
2659 /* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2660 PHP_FUNCTION(array_fill_keys)
2661 {
2662 zval *keys, *val, *entry;
2663
2664 ZEND_PARSE_PARAMETERS_START(2, 2)
2665 Z_PARAM_ARRAY(keys)
2666 Z_PARAM_ZVAL(val)
2667 ZEND_PARSE_PARAMETERS_END();
2668
2669 /* Initialize return array */
2670 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2671
2672 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2673 ZVAL_DEREF(entry);
2674 Z_TRY_ADDREF_P(val);
2675 if (Z_TYPE_P(entry) == IS_LONG) {
2676 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2677 } else {
2678 zend_string *tmp_key;
2679 zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2680 zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2681 zend_tmp_string_release(tmp_key);
2682 }
2683 } ZEND_HASH_FOREACH_END();
2684 }
2685 /* }}} */
2686
2687 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
2688 double __calc_size = ((start - end) / step) + 1; \
2689 if (__calc_size >= (double)HT_MAX_SIZE) { \
2690 zend_value_error(\
2691 "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
2692 RETURN_THROWS(); \
2693 } \
2694 size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2695 array_init_size(return_value, size); \
2696 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2697 } while (0)
2698
2699 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
2700 zend_ulong __calc_size = ((zend_ulong) start - end) / lstep; \
2701 if (__calc_size >= HT_MAX_SIZE - 1) { \
2702 zend_value_error(\
2703 "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
2704 RETURN_THROWS(); \
2705 } \
2706 size = (uint32_t)(__calc_size + 1); \
2707 array_init_size(return_value, size); \
2708 zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2709 } while (0)
2710
2711 /* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2712 PHP_FUNCTION(range)
2713 {
2714 zval *zlow, *zhigh, *zstep = NULL, tmp;
2715 int err = 0, is_step_double = 0;
2716 double step = 1.0;
2717
2718 ZEND_PARSE_PARAMETERS_START(2, 3)
2719 Z_PARAM_ZVAL(zlow)
2720 Z_PARAM_ZVAL(zhigh)
2721 Z_PARAM_OPTIONAL
2722 Z_PARAM_NUMBER(zstep)
2723 ZEND_PARSE_PARAMETERS_END();
2724
2725 if (zstep) {
2726 is_step_double = Z_TYPE_P(zstep) == IS_DOUBLE;
2727 step = zval_get_double(zstep);
2728
2729 /* We only want positive step values. */
2730 if (step < 0.0) {
2731 step *= -1;
2732 }
2733 }
2734
2735 /* If the range is given as strings, generate an array of characters. */
2736 if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
2737 int type1, type2;
2738 unsigned char low, high;
2739 zend_long lstep = (zend_long) step;
2740
2741 type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
2742 type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
2743
2744 if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
2745 goto double_str;
2746 } else if (type1 == IS_LONG || type2 == IS_LONG) {
2747 goto long_str;
2748 }
2749
2750 low = (unsigned char)Z_STRVAL_P(zlow)[0];
2751 high = (unsigned char)Z_STRVAL_P(zhigh)[0];
2752
2753 if (low > high) { /* Negative Steps */
2754 if (low - high < lstep || lstep <= 0) {
2755 err = 1;
2756 goto err;
2757 }
2758 /* Initialize the return_value as an array. */
2759 array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1));
2760 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2761 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2762 for (; low >= high; low -= (unsigned int)lstep) {
2763 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2764 ZEND_HASH_FILL_NEXT();
2765 if (((signed int)low - lstep) < 0) {
2766 break;
2767 }
2768 }
2769 } ZEND_HASH_FILL_END();
2770 } else if (high > low) { /* Positive Steps */
2771 if (high - low < lstep || lstep <= 0) {
2772 err = 1;
2773 goto err;
2774 }
2775 array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1));
2776 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2777 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2778 for (; low <= high; low += (unsigned int)lstep) {
2779 ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2780 ZEND_HASH_FILL_NEXT();
2781 if (((signed int)low + lstep) > 255) {
2782 break;
2783 }
2784 }
2785 } ZEND_HASH_FILL_END();
2786 } else {
2787 array_init(return_value);
2788 ZVAL_CHAR(&tmp, low);
2789 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2790 }
2791 } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
2792 double low, high, element;
2793 uint32_t i, size;
2794 double_str:
2795 low = zval_get_double(zlow);
2796 high = zval_get_double(zhigh);
2797
2798 if (zend_isinf(high) || zend_isinf(low)) {
2799 zend_value_error("Invalid range supplied: start=%0.0f end=%0.0f", low, high);
2800 RETURN_THROWS();
2801 }
2802
2803 if (low > high) { /* Negative steps */
2804 if (low - high < step || step <= 0) {
2805 err = 1;
2806 goto err;
2807 }
2808
2809 RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
2810
2811 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2812 for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step)) {
2813 ZEND_HASH_FILL_SET_DOUBLE(element);
2814 ZEND_HASH_FILL_NEXT();
2815 }
2816 } ZEND_HASH_FILL_END();
2817 } else if (high > low) { /* Positive steps */
2818 if (high - low < step || step <= 0) {
2819 err = 1;
2820 goto err;
2821 }
2822
2823 RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
2824
2825 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2826 for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step)) {
2827 ZEND_HASH_FILL_SET_DOUBLE(element);
2828 ZEND_HASH_FILL_NEXT();
2829 }
2830 } ZEND_HASH_FILL_END();
2831 } else {
2832 array_init(return_value);
2833 ZVAL_DOUBLE(&tmp, low);
2834 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2835 }
2836 } else {
2837 zend_long low, high;
2838 /* lstep is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
2839 zend_ulong lstep;
2840 uint32_t i, size;
2841 long_str:
2842 low = zval_get_long(zlow);
2843 high = zval_get_long(zhigh);
2844
2845 if (step <= 0) {
2846 err = 1;
2847 goto err;
2848 }
2849
2850 lstep = (zend_ulong)step;
2851 if (step <= 0) {
2852 err = 1;
2853 goto err;
2854 }
2855
2856 if (low > high) { /* Negative steps */
2857 if ((zend_ulong)low - high < lstep) {
2858 err = 1;
2859 goto err;
2860 }
2861
2862 RANGE_CHECK_LONG_INIT_ARRAY(low, high);
2863
2864 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2865 for (i = 0; i < size; ++i) {
2866 ZEND_HASH_FILL_SET_LONG(low - (i * lstep));
2867 ZEND_HASH_FILL_NEXT();
2868 }
2869 } ZEND_HASH_FILL_END();
2870 } else if (high > low) { /* Positive steps */
2871 if ((zend_ulong)high - low < lstep) {
2872 err = 1;
2873 goto err;
2874 }
2875
2876 RANGE_CHECK_LONG_INIT_ARRAY(high, low);
2877
2878 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2879 for (i = 0; i < size; ++i) {
2880 ZEND_HASH_FILL_SET_LONG(low + (i * lstep));
2881 ZEND_HASH_FILL_NEXT();
2882 }
2883 } ZEND_HASH_FILL_END();
2884 } else {
2885 array_init(return_value);
2886 ZVAL_LONG(&tmp, low);
2887 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2888 }
2889 }
2890 err:
2891 if (err) {
2892 zend_argument_value_error(3, "must not exceed the specified range");
2893 RETURN_THROWS();
2894 }
2895 }
2896 /* }}} */
2897
2898 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
2899 #undef RANGE_CHECK_LONG_INIT_ARRAY
2900
2901 static void php_array_data_shuffle(zval *array) /* {{{ */
2902 {
2903 uint32_t idx, j, n_elems;
2904 Bucket *p, temp;
2905 HashTable *hash;
2906 zend_long rnd_idx;
2907 uint32_t n_left;
2908
2909 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
2910
2911 if (n_elems < 1) {
2912 return;
2913 }
2914
2915 hash = Z_ARRVAL_P(array);
2916 n_left = n_elems;
2917
2918 if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
2919 if (hash->nNumUsed != hash->nNumOfElements) {
2920 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2921 p = hash->arData + idx;
2922 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2923 if (j != idx) {
2924 hash->arData[j] = *p;
2925 }
2926 j++;
2927 }
2928 }
2929 while (--n_left) {
2930 rnd_idx = php_mt_rand_range(0, n_left);
2931 if (rnd_idx != n_left) {
2932 temp = hash->arData[n_left];
2933 hash->arData[n_left] = hash->arData[rnd_idx];
2934 hash->arData[rnd_idx] = temp;
2935 }
2936 }
2937 } else {
2938 uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
2939
2940 if (hash->nNumUsed != hash->nNumOfElements) {
2941 for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2942 p = hash->arData + idx;
2943 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2944 if (j != idx) {
2945 hash->arData[j] = *p;
2946 if (idx == iter_pos) {
2947 zend_hash_iterators_update(hash, idx, j);
2948 iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
2949 }
2950 }
2951 j++;
2952 }
2953 }
2954 while (--n_left) {
2955 rnd_idx = php_mt_rand_range(0, n_left);
2956 if (rnd_idx != n_left) {
2957 temp = hash->arData[n_left];
2958 hash->arData[n_left] = hash->arData[rnd_idx];
2959 hash->arData[rnd_idx] = temp;
2960 zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
2961 }
2962 }
2963 }
2964 hash->nNumUsed = n_elems;
2965 hash->nInternalPointer = 0;
2966
2967 for (j = 0; j < n_elems; j++) {
2968 p = hash->arData + j;
2969 if (p->key) {
2970 zend_string_release_ex(p->key, 0);
2971 }
2972 p->h = j;
2973 p->key = NULL;
2974 }
2975 hash->nNextFreeElement = n_elems;
2976 if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
2977 zend_hash_to_packed(hash);
2978 }
2979 }
2980 /* }}} */
2981
2982 /* {{{ Randomly shuffle the contents of an array */
2983 PHP_FUNCTION(shuffle)
2984 {
2985 zval *array;
2986
2987 ZEND_PARSE_PARAMETERS_START(1, 1)
2988 Z_PARAM_ARRAY_EX(array, 0, 1)
2989 ZEND_PARSE_PARAMETERS_END();
2990
2991 php_array_data_shuffle(array);
2992
2993 RETURN_TRUE;
2994 }
2995 /* }}} */
2996
2997 static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
2998 {
2999 HashTable out_hash; /* Output hashtable */
3000 zend_long num_in; /* Number of entries in the input hashtable */
3001 zend_long pos; /* Current position in the hashtable */
3002 uint32_t idx;
3003 Bucket *p; /* Pointer to hash bucket */
3004 zval *entry; /* Hash entry */
3005 uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3006
3007 /* Get number of entries in the input hash */
3008 num_in = zend_hash_num_elements(in_hash);
3009
3010 /* Clamp the offset.. */
3011 if (offset > num_in) {
3012 offset = num_in;
3013 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3014 offset = 0;
3015 }
3016
3017 /* ..and the length */
3018 if (length < 0) {
3019 length = num_in - offset + length;
3020 } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3021 length = num_in - offset;
3022 }
3023
3024 /* Create and initialize output hash */
3025 zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
3026
3027 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3028 for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
3029 p = in_hash->arData + idx;
3030 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3031 entry = &p->val;
3032
3033 /* Update output hash depending on key type */
3034 if (p->key == NULL) {
3035 zend_hash_next_index_insert_new(&out_hash, entry);
3036 } else {
3037 zend_hash_add_new(&out_hash, p->key, entry);
3038 }
3039 if (idx == iter_pos) {
3040 if ((zend_long)idx != pos) {
3041 zend_hash_iterators_update(in_hash, idx, pos);
3042 }
3043 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3044 }
3045 pos++;
3046 }
3047
3048 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3049 if (removed != NULL) {
3050 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
3051 p = in_hash->arData + idx;
3052 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3053 pos++;
3054 entry = &p->val;
3055 Z_TRY_ADDREF_P(entry);
3056 if (p->key == NULL) {
3057 zend_hash_next_index_insert_new(removed, entry);
3058 } else {
3059 zend_hash_add_new(removed, p->key, entry);
3060 }
3061 zend_hash_del_bucket(in_hash, p);
3062 }
3063 } else { /* otherwise just skip those entries */
3064 int pos2 = pos;
3065
3066 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
3067 p = in_hash->arData + idx;
3068 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3069 pos2++;
3070 zend_hash_del_bucket(in_hash, p);
3071 }
3072 }
3073 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3074
3075 /* If there are entries to insert.. */
3076 if (replace) {
3077 ZEND_HASH_FOREACH_VAL(replace, entry) {
3078 Z_TRY_ADDREF_P(entry);
3079 zend_hash_next_index_insert_new(&out_hash, entry);
3080 pos++;
3081 } ZEND_HASH_FOREACH_END();
3082 }
3083
3084 /* Copy the remaining input hash entries to the output hash */
3085 for ( ; idx < in_hash->nNumUsed ; idx++) {
3086 p = in_hash->arData + idx;
3087 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3088 entry = &p->val;
3089 if (p->key == NULL) {
3090 zend_hash_next_index_insert_new(&out_hash, entry);
3091 } else {
3092 zend_hash_add_new(&out_hash, p->key, entry);
3093 }
3094 if (idx == iter_pos) {
3095 if ((zend_long)idx != pos) {
3096 zend_hash_iterators_update(in_hash, idx, pos);
3097 }
3098 iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3099 }
3100 pos++;
3101 }
3102
3103 /* replace HashTable data */
3104 HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3105 HT_SET_ITERATORS_COUNT(in_hash, 0);
3106 in_hash->pDestructor = NULL;
3107 zend_hash_destroy(in_hash);
3108
3109 HT_FLAGS(in_hash) = HT_FLAGS(&out_hash);
3110 in_hash->nTableSize = out_hash.nTableSize;
3111 in_hash->nTableMask = out_hash.nTableMask;
3112 in_hash->nNumUsed = out_hash.nNumUsed;
3113 in_hash->nNumOfElements = out_hash.nNumOfElements;
3114 in_hash->nNextFreeElement = out_hash.nNextFreeElement;
3115 in_hash->arData = out_hash.arData;
3116 in_hash->pDestructor = out_hash.pDestructor;
3117
3118 zend_hash_internal_pointer_reset(in_hash);
3119 }
3120 /* }}} */
3121
3122 /* {{{ Pushes elements onto the end of the array */
3123 PHP_FUNCTION(array_push)
3124 {
3125 zval *args, /* Function arguments array */
3126 *stack, /* Input array */
3127 new_var; /* Variable to be pushed */
3128 int i, /* Loop counter */
3129 argc; /* Number of function arguments */
3130
3131
3132 ZEND_PARSE_PARAMETERS_START(1, -1)
3133 Z_PARAM_ARRAY_EX(stack, 0, 1)
3134 Z_PARAM_VARIADIC('+', args, argc)
3135 ZEND_PARSE_PARAMETERS_END();
3136
3137 /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3138 for (i = 0; i < argc; i++) {
3139 ZVAL_COPY(&new_var, &args[i]);
3140
3141 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
3142 Z_TRY_DELREF(new_var);
3143 zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3144 RETURN_THROWS();
3145 }
3146 }
3147
3148 /* Clean up and return the number of values in the stack */
3149 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3150 }
3151 /* }}} */
3152
3153 /* {{{ Pops an element off the end of the array */
3154 PHP_FUNCTION(array_pop)
3155 {
3156 zval *stack, /* Input stack */
3157 *val; /* Value to be popped */
3158 uint32_t idx;
3159 Bucket *p;
3160
3161 ZEND_PARSE_PARAMETERS_START(1, 1)
3162 Z_PARAM_ARRAY_EX(stack, 0, 1)
3163 ZEND_PARSE_PARAMETERS_END();
3164
3165 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3166 return;
3167 }
3168
3169 /* Get the last value and copy it into the return value */
3170 idx = Z_ARRVAL_P(stack)->nNumUsed;
3171 while (1) {
3172 if (idx == 0) {
3173 return;
3174 }
3175 idx--;
3176 p = Z_ARRVAL_P(stack)->arData + idx;
3177 val = &p->val;
3178 if (Z_TYPE_P(val) != IS_UNDEF) {
3179 break;
3180 }
3181 }
3182 RETVAL_COPY_DEREF(val);
3183
3184 if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3185 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3186 }
3187
3188 /* Delete the last value */
3189 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3190 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3191 }
3192 /* }}} */
3193
3194 /* {{{ Pops an element off the beginning of the array */
3195 PHP_FUNCTION(array_shift)
3196 {
3197 zval *stack, /* Input stack */
3198 *val; /* Value to be popped */
3199 uint32_t idx;
3200 Bucket *p;
3201
3202 ZEND_PARSE_PARAMETERS_START(1, 1)
3203 Z_PARAM_ARRAY_EX(stack, 0, 1)
3204 ZEND_PARSE_PARAMETERS_END();
3205
3206 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3207 return;
3208 }
3209
3210 /* Get the first value and copy it into the return value */
3211 idx = 0;
3212 while (1) {
3213 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3214 return;
3215 }
3216 p = Z_ARRVAL_P(stack)->arData + idx;
3217 val = &p->val;
3218 if (Z_TYPE_P(val) != IS_UNDEF) {
3219 break;
3220 }
3221 idx++;
3222 }
3223 RETVAL_COPY_DEREF(val);
3224
3225 /* Delete the first value */
3226 zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3227
3228 /* re-index like it did before */
3229 if (HT_FLAGS(Z_ARRVAL_P(stack)) & HASH_FLAG_PACKED) {
3230 uint32_t k = 0;
3231
3232 if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3233 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3234 p = Z_ARRVAL_P(stack)->arData + idx;
3235 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3236 if (idx != k) {
3237 Bucket *q = Z_ARRVAL_P(stack)->arData + k;
3238 q->h = k;
3239 q->key = NULL;
3240 ZVAL_COPY_VALUE(&q->val, &p->val);
3241 ZVAL_UNDEF(&p->val);
3242 }
3243 k++;
3244 }
3245 } else {
3246 uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3247
3248 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3249 p = Z_ARRVAL_P(stack)->arData + idx;
3250 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3251 if (idx != k) {
3252 Bucket *q = Z_ARRVAL_P(stack)->arData + k;
3253 q->h = k;
3254 q->key = NULL;
3255 ZVAL_COPY_VALUE(&q->val, &p->val);
3256 ZVAL_UNDEF(&p->val);
3257 if (idx == iter_pos) {
3258 zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3259 iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3260 }
3261 }
3262 k++;
3263 }
3264 }
3265 Z_ARRVAL_P(stack)->nNumUsed = k;
3266 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3267 } else {
3268 uint32_t k = 0;
3269 int should_rehash = 0;
3270
3271 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3272 p = Z_ARRVAL_P(stack)->arData + idx;
3273 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3274 if (p->key == NULL) {
3275 if (p->h != k) {
3276 p->h = k++;
3277 should_rehash = 1;
3278 } else {
3279 k++;
3280 }
3281 }
3282 }
3283 Z_ARRVAL_P(stack)->nNextFreeElement = k;
3284 if (should_rehash) {
3285 zend_hash_rehash(Z_ARRVAL_P(stack));
3286 }
3287 }
3288
3289 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3290 }
3291 /* }}} */
3292
3293 /* {{{ Pushes elements onto the beginning of the array */
3294 PHP_FUNCTION(array_unshift)
3295 {
3296 zval *args, /* Function arguments array */
3297 *stack; /* Input stack */
3298 HashTable new_hash; /* New hashtable for the stack */
3299 int argc; /* Number of function arguments */
3300 int i;
3301 zend_string *key;
3302 zval *value;
3303
3304 ZEND_PARSE_PARAMETERS_START(1, -1)
3305 Z_PARAM_ARRAY_EX(stack, 0, 1)
3306 Z_PARAM_VARIADIC('+', args, argc)
3307 ZEND_PARSE_PARAMETERS_END();
3308
3309 zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3310 for (i = 0; i < argc; i++) {
3311 Z_TRY_ADDREF(args[i]);
3312 zend_hash_next_index_insert_new(&new_hash, &args[i]);
3313 }
3314
3315 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3316 if (key) {
3317 zend_hash_add_new(&new_hash, key, value);
3318 } else {
3319 zend_hash_next_index_insert_new(&new_hash, value);
3320 }
3321 } ZEND_HASH_FOREACH_END();
3322
3323 if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3324 zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3325 HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3326 HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3327 }
3328
3329 /* replace HashTable data */
3330 Z_ARRVAL_P(stack)->pDestructor = NULL;
3331 zend_hash_destroy(Z_ARRVAL_P(stack));
3332
3333 HT_FLAGS(Z_ARRVAL_P(stack)) = HT_FLAGS(&new_hash);
3334 Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
3335 Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
3336 Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
3337 Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
3338 Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
3339 Z_ARRVAL_P(stack)->arData = new_hash.arData;
3340 Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
3341
3342 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3343
3344 /* Clean up and return the number of elements in the stack */
3345 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3346 }
3347 /* }}} */
3348
3349 /* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3350 PHP_FUNCTION(array_splice)
3351 {
3352 zval *array, /* Input array */
3353 *repl_array = NULL; /* Replacement array */
3354 HashTable *rem_hash = NULL;
3355 zend_long offset,
3356 length = 0;
3357 bool length_is_null = 1;
3358 int num_in; /* Number of elements in the input array */
3359
3360 ZEND_PARSE_PARAMETERS_START(2, 4)
3361 Z_PARAM_ARRAY_EX(array, 0, 1)
3362 Z_PARAM_LONG(offset)
3363 Z_PARAM_OPTIONAL
3364 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3365 Z_PARAM_ZVAL(repl_array)
3366 ZEND_PARSE_PARAMETERS_END();
3367
3368 num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3369
3370 if (length_is_null) {
3371 length = num_in;
3372 }
3373
3374 if (ZEND_NUM_ARGS() == 4) {
3375 /* Make sure the last argument, if passed, is an array */
3376 convert_to_array(repl_array);
3377 }
3378
3379 /* Don't create the array of removed elements if it's not going
3380 * to be used; e.g. only removing and/or replacing elements */
3381 if (USED_RET()) {
3382 zend_long size = length;
3383
3384 /* Clamp the offset.. */
3385 if (offset > num_in) {
3386 offset = num_in;
3387 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3388 offset = 0;
3389 }
3390
3391 /* ..and the length */
3392 if (length < 0) {
3393 size = num_in - offset + length;
3394 } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
3395 size = num_in - offset;
3396 }
3397
3398 /* Initialize return value */
3399 array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3400 rem_hash = Z_ARRVAL_P(return_value);
3401 } else {
3402 /* The return value will not be used, but make sure it still has the correct type. */
3403 RETVAL_EMPTY_ARRAY();
3404 }
3405
3406 /* Perform splice */
3407 php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3408 }
3409 /* }}} */
3410
3411 /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3412 Finds the bucket at the given valid offset */
3413 static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3414 {
3415 zend_long pos;
3416 Bucket *bucket;
3417 ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3418 if (HT_IS_WITHOUT_HOLES(ht)) {
3419 /* There's no need to iterate over the array to filter out holes if there are no holes */
3420 /* This properly handles both packed and unpacked arrays. */
3421 return ht->arData + offset;
3422 }
3423 /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3424 pos = 0;
3425 ZEND_HASH_FOREACH_BUCKET(ht, bucket) {
3426 if (pos >= offset) {
3427 /* This is the bucket of the array element at the requested offset */
3428 return bucket;
3429 }
3430 ++pos;
3431 } ZEND_HASH_FOREACH_END();
3432
3433 /* Return a pointer to the end of the bucket array. */
3434 return ht->arData + ht->nNumUsed;
3435 }
3436 /* }}} */
3437
3438 /* {{{ Returns elements specified by offset and length */
3439 PHP_FUNCTION(array_slice)
3440 {
3441 zval *input; /* Input array */
3442 zval *entry; /* An array entry */
3443 zend_long offset; /* Offset to get elements from */
3444 zend_long length = 0; /* How many elements to get */
3445 bool length_is_null = 1; /* Whether an explicit length has been omitted */
3446 bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array */
3447 uint32_t num_in; /* Number of elements in the input array */
3448 zend_string *string_key;
3449 zend_ulong num_key;
3450
3451 ZEND_PARSE_PARAMETERS_START(2, 4)
3452 Z_PARAM_ARRAY(input)
3453 Z_PARAM_LONG(offset)
3454 Z_PARAM_OPTIONAL
3455 Z_PARAM_LONG_OR_NULL(length, length_is_null)
3456 Z_PARAM_BOOL(preserve_keys)
3457 ZEND_PARSE_PARAMETERS_END();
3458
3459 /* Get number of entries in the input hash */
3460 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
3461
3462 /* We want all entries from offset to the end if length is not passed or is null */
3463 if (length_is_null) {
3464 length = num_in;
3465 }
3466
3467 /* Clamp the offset.. */
3468 if (offset > (zend_long) num_in) {
3469 RETURN_EMPTY_ARRAY();
3470 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3471 offset = 0;
3472 }
3473
3474 /* ..and the length */
3475 if (length < 0) {
3476 length = num_in - offset + length;
3477 } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3478 length = num_in - offset;
3479 }
3480
3481 if (length <= 0) {
3482 RETURN_EMPTY_ARRAY();
3483 }
3484
3485 /* Initialize returned array */
3486 array_init_size(return_value, (uint32_t)length);
3487
3488 // Contains modified variants of ZEND_HASH_FOREACH_VAL
3489 {
3490 HashTable *ht = Z_ARRVAL_P(input);
3491 Bucket *p = find_bucket_at_offset(ht, offset);
3492 Bucket *end = ht->arData + ht->nNumUsed;
3493
3494 /* Start at the beginning and go until we hit offset */
3495 if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
3496 (!preserve_keys ||
3497 (offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
3498
3499 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3500 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3501 for (; p != end; p++) {
3502 if (__fill_idx >= length) {
3503 break;
3504 }
3505 entry = &p->val;
3506 if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3507 continue;
3508 }
3509 if (UNEXPECTED(Z_ISREF_P(entry)) &&
3510 UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3511 entry = Z_REFVAL_P(entry);
3512 }
3513 Z_TRY_ADDREF_P(entry);
3514 ZEND_HASH_FILL_ADD(entry);
3515 }
3516 } ZEND_HASH_FILL_END();
3517 } else {
3518 zend_long n = 0; /* Current number of elements */
3519 for (; p != end; p++) {
3520 entry = &p->val;
3521 if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3522 continue;
3523 }
3524 if (n >= length) {
3525 break;
3526 }
3527 n++;
3528 num_key = p->h;
3529 string_key = p->key;
3530
3531 if (string_key) {
3532 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3533 } else {
3534 if (preserve_keys) {
3535 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3536 } else {
3537 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3538 }
3539 }
3540 zval_add_ref(entry);
3541 }
3542 }
3543 }
3544 }
3545 /* }}} */
3546
3547 PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
3548 {
3549 zval *src_entry, *dest_entry;
3550 zend_string *string_key;
3551
3552 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3553 if (string_key) {
3554 if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
3555 zval *src_zval = src_entry;
3556 zval *dest_zval = dest_entry;
3557 HashTable *thash;
3558 zval tmp;
3559 int ret;
3560
3561 ZVAL_DEREF(src_zval);
3562 ZVAL_DEREF(dest_zval);
3563 thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
3564 if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
3565 zend_throw_error(NULL, "Recursion detected");
3566 return 0;
3567 }
3568
3569 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
3570 SEPARATE_ZVAL(dest_entry);
3571 dest_zval = dest_entry;
3572
3573 if (Z_TYPE_P(dest_zval) == IS_NULL) {
3574 convert_to_array(dest_zval);
3575 add_next_index_null(dest_zval);
3576 } else {
3577 convert_to_array(dest_zval);
3578 }
3579 ZVAL_UNDEF(&tmp);
3580 if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3581 ZVAL_COPY(&tmp, src_zval);
3582 convert_to_array(&tmp);
3583 src_zval = &tmp;
3584 }
3585 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3586 if (thash) {
3587 GC_TRY_PROTECT_RECURSION(thash);
3588 }
3589 ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3590 if (thash) {
3591 GC_TRY_UNPROTECT_RECURSION(thash);
3592 }
3593 if (!ret) {
3594 return 0;
3595 }
3596 } else {
3597 Z_TRY_ADDREF_P(src_zval);
3598 zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3599 if (EXPECTED(!zv)) {
3600 Z_TRY_DELREF_P(src_zval);
3601 zend_cannot_add_element();
3602 return 0;
3603 }
3604 }
3605 zval_ptr_dtor(&tmp);
3606 } else {
3607 zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3608 zval_add_ref(zv);
3609 }
3610 } else {
3611 zval *zv = zend_hash_next_index_insert(dest, src_entry);
3612 if (UNEXPECTED(!zv)) {
3613 zend_cannot_add_element();
3614 return 0;
3615 }
3616 zval_add_ref(zv);
3617 }
3618 } ZEND_HASH_FOREACH_END();
3619 return 1;
3620 }
3621 /* }}} */
3622
3623 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3624 {
3625 zval *src_entry;
3626 zend_string *string_key;
3627
3628 if ((HT_FLAGS(dest) & HASH_FLAG_PACKED) && (HT_FLAGS(src) & HASH_FLAG_PACKED)) {
3629 zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
3630 ZEND_HASH_FILL_PACKED(dest) {
3631 ZEND_HASH_FOREACH_VAL(src, src_entry) {
3632 if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
3633 UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
3634 src_entry = Z_REFVAL_P(src_entry);
3635 }
3636 Z_TRY_ADDREF_P(src_entry);
3637 ZEND_HASH_FILL_ADD(src_entry);
3638 } ZEND_HASH_FOREACH_END();
3639 } ZEND_HASH_FILL_END();
3640 } else {
3641 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3642 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3643 Z_REFCOUNT_P(src_entry) == 1)) {
3644 src_entry = Z_REFVAL_P(src_entry);
3645 }
3646 Z_TRY_ADDREF_P(src_entry);
3647 if (string_key) {
3648 zend_hash_update(dest, string_key, src_entry);
3649 } else {
3650 zend_hash_next_index_insert_new(dest, src_entry);
3651 }
3652 } ZEND_HASH_FOREACH_END();
3653 }
3654 return 1;
3655 }
3656 /* }}} */
3657
3658 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
3659 {
3660 zval *src_entry, *dest_entry, *src_zval, *dest_zval;
3661 zend_string *string_key;
3662 zend_ulong num_key;
3663 int ret;
3664
3665 ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
3666 src_zval = src_entry;
3667 ZVAL_DEREF(src_zval);
3668 if (string_key) {
3669 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3670 (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
3671 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
3672 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3673
3674 zval *zv = zend_hash_update(dest, string_key, src_entry);
3675 zval_add_ref(zv);
3676 continue;
3677 }
3678 } else {
3679 if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3680 (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
3681 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
3682 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3683
3684 zval *zv = zend_hash_index_update(dest, num_key, src_entry);
3685 zval_add_ref(zv);
3686 continue;
3687 }
3688 }
3689
3690 dest_zval = dest_entry;
3691 ZVAL_DEREF(dest_zval);
3692 if (Z_IS_RECURSIVE_P(dest_zval) ||
3693 Z_IS_RECURSIVE_P(src_zval) ||
3694 (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
3695 zend_throw_error(NULL, "Recursion detected");
3696 return 0;
3697 }
3698
3699 ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
3700 SEPARATE_ZVAL(dest_entry);
3701 dest_zval = dest_entry;
3702
3703 if (Z_REFCOUNTED_P(dest_zval)) {
3704 Z_PROTECT_RECURSION_P(dest_zval);
3705 }
3706 if (Z_REFCOUNTED_P(src_zval)) {
3707 Z_PROTECT_RECURSION_P(src_zval);
3708 }
3709
3710 ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3711
3712 if (Z_REFCOUNTED_P(dest_zval)) {
3713 Z_UNPROTECT_RECURSION_P(dest_zval);
3714 }
3715 if (Z_REFCOUNTED_P(src_zval)) {
3716 Z_UNPROTECT_RECURSION_P(src_zval);
3717 }
3718
3719 if (!ret) {
3720 return 0;
3721 }
3722 } ZEND_HASH_FOREACH_END();
3723
3724 return 1;
3725 }
3726 /* }}} */
3727
3728 static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
3729 {
3730 zval *args = NULL;
3731 zval *arg;
3732 int argc, i;
3733 HashTable *dest;
3734
3735 ZEND_PARSE_PARAMETERS_START(1, -1)
3736 Z_PARAM_VARIADIC('+', args, argc)
3737 ZEND_PARSE_PARAMETERS_END();
3738
3739
3740 for (i = 0; i < argc; i++) {
3741 zval *arg = args + i;
3742
3743 if (Z_TYPE_P(arg) != IS_ARRAY) {
3744 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
3745 RETURN_THROWS();
3746 }
3747 }
3748
3749 /* copy first array */
3750 arg = args;
3751 dest = zend_array_dup(Z_ARRVAL_P(arg));
3752 ZVAL_ARR(return_value, dest);
3753 if (recursive) {
3754 for (i = 1; i < argc; i++) {
3755 arg = args + i;
3756 php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
3757 }
3758 } else {
3759 for (i = 1; i < argc; i++) {
3760 arg = args + i;
3761 zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
3762 }
3763 }
3764 }
3765 /* }}} */
3766
3767 static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
3768 {
3769 zval *args = NULL;
3770 zval *arg;
3771 int argc, i;
3772 zval *src_entry;
3773 HashTable *src, *dest;
3774 uint32_t count = 0;
3775
3776 ZEND_PARSE_PARAMETERS_START(0, -1)
3777 Z_PARAM_VARIADIC('+', args, argc)
3778 ZEND_PARSE_PARAMETERS_END();
3779
3780 if (argc == 0) {
3781 RETURN_EMPTY_ARRAY();
3782 }
3783
3784 for (i = 0; i < argc; i++) {
3785 zval *arg = args + i;
3786
3787 if (Z_TYPE_P(arg) != IS_ARRAY) {
3788 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
3789 RETURN_THROWS();
3790 }
3791 count += zend_hash_num_elements(Z_ARRVAL_P(arg));
3792 }
3793
3794 if (argc == 2) {
3795 zval *ret = NULL;
3796
3797 if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
3798 ret = &args[1];
3799 } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
3800 ret = &args[0];
3801 }
3802 if (ret) {
3803 if (HT_FLAGS(Z_ARRVAL_P(ret)) & HASH_FLAG_PACKED) {
3804 if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
3805 ZVAL_COPY(return_value, ret);
3806 return;
3807 }
3808 } else {
3809 bool copy = 1;
3810 zend_string *string_key;
3811
3812 ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
3813 if (!string_key) {
3814 copy = 0;
3815 break;
3816 }
3817 } ZEND_HASH_FOREACH_END();
3818 if (copy) {
3819 ZVAL_COPY(return_value, ret);
3820 return;
3821 }
3822 }
3823 }
3824 }
3825
3826 arg = args;
3827 src = Z_ARRVAL_P(arg);
3828 /* copy first array */
3829 array_init_size(return_value, count);
3830 dest = Z_ARRVAL_P(return_value);
3831 if (HT_FLAGS(src) & HASH_FLAG_PACKED) {
3832 zend_hash_real_init_packed(dest);
3833 ZEND_HASH_FILL_PACKED(dest) {
3834 ZEND_HASH_FOREACH_VAL(src, src_entry) {
3835 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3836 Z_REFCOUNT_P(src_entry) == 1)) {
3837 src_entry = Z_REFVAL_P(src_entry);
3838 }
3839 Z_TRY_ADDREF_P(src_entry);
3840 ZEND_HASH_FILL_ADD(src_entry);
3841 } ZEND_HASH_FOREACH_END();
3842 } ZEND_HASH_FILL_END();
3843 } else {
3844 zend_string *string_key;
3845 zend_hash_real_init_mixed(dest);
3846 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3847 if (UNEXPECTED(Z_ISREF_P(src_entry) &&
3848 Z_REFCOUNT_P(src_entry) == 1)) {
3849 src_entry = Z_REFVAL_P(src_entry);
3850 }
3851 Z_TRY_ADDREF_P(src_entry);
3852 if (EXPECTED(string_key)) {
3853 _zend_hash_append(dest, string_key, src_entry);
3854 } else {
3855 zend_hash_next_index_insert_new(dest, src_entry);
3856 }
3857 } ZEND_HASH_FOREACH_END();
3858 }
3859 if (recursive) {
3860 for (i = 1; i < argc; i++) {
3861 arg = args + i;
3862 php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
3863 }
3864 } else {
3865 for (i = 1; i < argc; i++) {
3866 arg = args + i;
3867 php_array_merge(dest, Z_ARRVAL_P(arg));
3868 }
3869 }
3870 }
3871 /* }}} */
3872
3873 /* {{{ Merges elements from passed arrays into one array */
3874 PHP_FUNCTION(array_merge)
3875 {
3876 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3877 }
3878 /* }}} */
3879
3880 /* {{{ Recursively merges elements from passed arrays into one array */
3881 PHP_FUNCTION(array_merge_recursive)
3882 {
3883 php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3884 }
3885 /* }}} */
3886
3887 /* {{{ Replaces elements from passed arrays into one array */
3888 PHP_FUNCTION(array_replace)
3889 {
3890 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3891 }
3892 /* }}} */
3893
3894 /* {{{ Recursively replaces elements from passed arrays into one array */
3895 PHP_FUNCTION(array_replace_recursive)
3896 {
3897 php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3898 }
3899 /* }}} */
3900
3901 /* {{{ Return just the keys from the input array, optionally only for the specified search_value */
3902 PHP_FUNCTION(array_keys)
3903 {
3904 zval *input, /* Input array */
3905 *search_value = NULL, /* Value to search for */
3906 *entry, /* An entry in the input array */
3907 new_val; /* New value */
3908 bool strict = 0; /* do strict comparison */
3909 zend_ulong num_idx;
3910 zend_string *str_idx;
3911 zend_array *arrval;
3912 zend_ulong elem_count;
3913
3914 ZEND_PARSE_PARAMETERS_START(1, 3)
3915 Z_PARAM_ARRAY(input)
3916 Z_PARAM_OPTIONAL
3917 Z_PARAM_ZVAL(search_value)
3918 Z_PARAM_BOOL(strict)
3919 ZEND_PARSE_PARAMETERS_END();
3920 arrval = Z_ARRVAL_P(input);
3921 elem_count = zend_hash_num_elements(arrval);
3922
3923 /* Base case: empty input */
3924 if (!elem_count) {
3925 RETURN_COPY(input);
3926 }
3927
3928 /* Initialize return array */
3929 if (search_value != NULL) {
3930 array_init(return_value);
3931
3932 if (strict) {
3933 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
3934 ZVAL_DEREF(entry);
3935 if (fast_is_identical_function(search_value, entry)) {
3936 if (str_idx) {
3937 ZVAL_STR_COPY(&new_val, str_idx);
3938 } else {
3939 ZVAL_LONG(&new_val, num_idx);
3940 }
3941 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
3942 }
3943 } ZEND_HASH_FOREACH_END();
3944 } else {
3945 ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
3946 if (fast_equal_check_function(search_value, entry)) {
3947 if (str_idx) {
3948 ZVAL_STR_COPY(&new_val, str_idx);
3949 } else {
3950 ZVAL_LONG(&new_val, num_idx);
3951 }
3952 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
3953 }
3954 } ZEND_HASH_FOREACH_END();
3955 }
3956 } else {
3957 array_init_size(return_value, elem_count);
3958 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3959 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3960 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
3961 /* Optimistic case: range(0..n-1) for vector-like packed array */
3962 zend_ulong lval = 0;
3963
3964 for (; lval < elem_count; ++lval) {
3965 ZEND_HASH_FILL_SET_LONG(lval);
3966 ZEND_HASH_FILL_NEXT();
3967 }
3968 } else {
3969 /* Go through input array and add keys to the return array */
3970 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3971 if (str_idx) {
3972 ZEND_HASH_FILL_SET_STR_COPY(str_idx);
3973 } else {
3974 ZEND_HASH_FILL_SET_LONG(num_idx);
3975 }
3976 ZEND_HASH_FILL_NEXT();
3977 } ZEND_HASH_FOREACH_END();
3978 }
3979 } ZEND_HASH_FILL_END();
3980 }
3981 }
3982 /* }}} */
3983
3984 /* {{{ Get the key of the first element of the array */
3985 PHP_FUNCTION(array_key_first)
3986 {
3987 zval *stack; /* Input stack */
3988
3989 ZEND_PARSE_PARAMETERS_START(1, 1)
3990 Z_PARAM_ARRAY(stack)
3991 ZEND_PARSE_PARAMETERS_END();
3992
3993 HashTable *target_hash = Z_ARRVAL_P (stack);
3994 HashPosition pos = 0;
3995 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
3996 }
3997 /* }}} */
3998
3999 /* {{{ Get the key of the last element of the array */
4000 PHP_FUNCTION(array_key_last)
4001 {
4002 zval *stack; /* Input stack */
4003 HashPosition pos;
4004
4005 ZEND_PARSE_PARAMETERS_START(1, 1)
4006 Z_PARAM_ARRAY(stack)
4007 ZEND_PARSE_PARAMETERS_END();
4008
4009 HashTable *target_hash = Z_ARRVAL_P (stack);
4010 zend_hash_internal_pointer_end_ex(target_hash, &pos);
4011 zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4012 }
4013 /* }}} */
4014
4015 /* {{{ Return just the values from the input array */
4016 PHP_FUNCTION(array_values)
4017 {
4018 zval *input, /* Input array */
4019 *entry; /* An entry in the input array */
4020 zend_array *arrval;
4021 zend_long arrlen;
4022
4023 ZEND_PARSE_PARAMETERS_START(1, 1)
4024 Z_PARAM_ARRAY(input)
4025 ZEND_PARSE_PARAMETERS_END();
4026
4027 arrval = Z_ARRVAL_P(input);
4028
4029 /* Return empty input as is */
4030 arrlen = zend_hash_num_elements(arrval);
4031 if (!arrlen) {
4032 RETURN_EMPTY_ARRAY();
4033 }
4034
4035 /* Return vector-like packed arrays as-is */
4036 if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4037 arrval->nNextFreeElement == arrlen) {
4038 RETURN_COPY(input);
4039 }
4040
4041 /* Initialize return array */
4042 array_init_size(return_value, arrlen);
4043 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4044
4045 /* Go through input array and add values to the return array */
4046 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4047 ZEND_HASH_FOREACH_VAL(arrval, entry) {
4048 if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
4049 entry = Z_REFVAL_P(entry);
4050 }
4051 Z_TRY_ADDREF_P(entry);
4052 ZEND_HASH_FILL_ADD(entry);
4053 } ZEND_HASH_FOREACH_END();
4054 } ZEND_HASH_FILL_END();
4055 }
4056 /* }}} */
4057
4058 /* {{{ Return the value as key and the frequency of that value in input as value */
4059 PHP_FUNCTION(array_count_values)
4060 {
4061 zval *input, /* Input array */
4062 *entry, /* An entry in the input array */
4063 *tmp;
4064 HashTable *myht;
4065
4066 ZEND_PARSE_PARAMETERS_START(1, 1)
4067 Z_PARAM_ARRAY(input)
4068 ZEND_PARSE_PARAMETERS_END();
4069
4070 /* Initialize return array */
4071 array_init(return_value);
4072
4073 /* Go through input array and add values to the return array */
4074 myht = Z_ARRVAL_P(input);
4075 ZEND_HASH_FOREACH_VAL(myht, entry) {
4076 ZVAL_DEREF(entry);
4077 if (Z_TYPE_P(entry) == IS_LONG) {
4078 if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4079 zval data;
4080 ZVAL_LONG(&data, 1);
4081 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4082 } else {
4083 Z_LVAL_P(tmp)++;
4084 }
4085 } else if (Z_TYPE_P(entry) == IS_STRING) {
4086 if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4087 zval data;
4088 ZVAL_LONG(&data, 1);
4089 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4090 } else {
4091 Z_LVAL_P(tmp)++;
4092 }
4093 } else {
4094 php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4095 }
4096 } ZEND_HASH_FOREACH_END();
4097 }
4098 /* }}} */
4099
4100 static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4101 {
4102 zval *prop = NULL;
4103
4104 if (Z_TYPE_P(data) == IS_OBJECT) {
4105 zend_string *tmp_str;
4106 /* If name is an integer convert integer to string */
4107 if (name_str == NULL) {
4108 tmp_str = zend_long_to_str(name_long);
4109 } else {
4110 tmp_str = zend_string_copy(name_str);
4111 }
4112 /* The has_property check is first performed in "exists" mode (which returns true for
4113 * properties that are null but exist) and then in "has" mode to handle objects that
4114 * implement __isset (which is not called in "exists" mode). */
4115 if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4116 || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4117 prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4118 if (prop) {
4119 ZVAL_DEREF(prop);
4120 if (prop != rv) {
4121 Z_TRY_ADDREF_P(prop);
4122 }
4123 }
4124 }
4125 zend_string_release(tmp_str);
4126 } else if (Z_TYPE_P(data) == IS_ARRAY) {
4127 /* Name is a string */
4128 if (name_str != NULL) {
4129 prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4130 } else {
4131 prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4132 }
4133 if (prop) {
4134 ZVAL_DEREF(prop);
4135 Z_TRY_ADDREF_P(prop);
4136 }
4137 }
4138
4139 return prop;
4140 }
4141 /* }}} */
4142
4143 /* {{{ Return the values from a single column in the input array, identified by the
4144 value_key and optionally indexed by the index_key */
4145 PHP_FUNCTION(array_column)
4146 {
4147 HashTable *input;
4148 zval *colval, *data, rv;
4149 zend_string *column_str = NULL;
4150 zend_long column_long;
4151 bool column_is_null = 0;
4152 zend_string *index_str = NULL;
4153 zend_long index_long;
4154 bool index_is_null = 1;
4155
4156 ZEND_PARSE_PARAMETERS_START(2, 3)
4157 Z_PARAM_ARRAY_HT(input)
4158 Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4159 Z_PARAM_OPTIONAL
4160 Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4161 ZEND_PARSE_PARAMETERS_END();
4162
4163 void* cache_slot_column[3] = { NULL, NULL, NULL };
4164 void* cache_slot_index[3] = { NULL, NULL, NULL };
4165
4166 array_init_size(return_value, zend_hash_num_elements(input));
4167 /* Index param is not passed */
4168 if (index_is_null) {
4169 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4170 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4171 ZEND_HASH_FOREACH_VAL(input, data) {
4172 ZVAL_DEREF(data);
4173 if (column_is_null) {
4174 Z_TRY_ADDREF_P(data);
4175 colval = data;
4176 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4177 continue;
4178 }
4179 ZEND_HASH_FILL_ADD(colval);
4180 } ZEND_HASH_FOREACH_END();
4181 } ZEND_HASH_FILL_END();
4182 } else {
4183 ZEND_HASH_FOREACH_VAL(input, data) {
4184 ZVAL_DEREF(data);
4185
4186 if (column_is_null) {
4187 Z_TRY_ADDREF_P(data);
4188 colval = data;
4189 } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4190 continue;
4191 }
4192
4193 zval rv;
4194 zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4195 if (keyval) {
4196 array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4197 zval_ptr_dtor(colval);
4198 zval_ptr_dtor(keyval);
4199 } else {
4200 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4201 }
4202 } ZEND_HASH_FOREACH_END();
4203 }
4204 }
4205 /* }}} */
4206
4207 /* {{{ Return input as a new array with the order of the entries reversed */
4208 PHP_FUNCTION(array_reverse)
4209 {
4210 zval *input, /* Input array */
4211 *entry; /* An entry in the input array */
4212 zend_string *string_key;
4213 zend_ulong num_key;
4214 bool preserve_keys = 0; /* whether to preserve keys */
4215
4216 ZEND_PARSE_PARAMETERS_START(1, 2)
4217 Z_PARAM_ARRAY(input)
4218 Z_PARAM_OPTIONAL
4219 Z_PARAM_BOOL(preserve_keys)
4220 ZEND_PARSE_PARAMETERS_END();
4221
4222 /* Initialize return array */
4223 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4224 if ((HT_FLAGS(Z_ARRVAL_P(input)) & HASH_FLAG_PACKED) && !preserve_keys) {
4225 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4226 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4227 ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4228 if (UNEXPECTED(Z_ISREF_P(entry) &&
4229 Z_REFCOUNT_P(entry) == 1)) {
4230 entry = Z_REFVAL_P(entry);
4231 }
4232 Z_TRY_ADDREF_P(entry);
4233 ZEND_HASH_FILL_ADD(entry);
4234 } ZEND_HASH_FOREACH_END();
4235 } ZEND_HASH_FILL_END();
4236 } else {
4237 ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4238 if (string_key) {
4239 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4240 } else {
4241 if (preserve_keys) {
4242 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4243 } else {
4244 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4245 }
4246 }
4247 zval_add_ref(entry);
4248 } ZEND_HASH_FOREACH_END();
4249 }
4250 }
4251 /* }}} */
4252
4253 /* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4254 PHP_FUNCTION(array_pad)
4255 {
4256 zval *input; /* Input array */
4257 zval *pad_value; /* Padding value obviously */
4258 zend_long pad_size; /* Size to pad to */
4259 zend_long pad_size_abs; /* Absolute value of pad_size */
4260 zend_long input_size; /* Size of the input array */
4261 zend_long num_pads; /* How many pads do we need */
4262 zend_long i;
4263 zend_string *key;
4264 zval *value;
4265
4266 ZEND_PARSE_PARAMETERS_START(3, 3)
4267 Z_PARAM_ARRAY(input)
4268 Z_PARAM_LONG(pad_size)
4269 Z_PARAM_ZVAL(pad_value)
4270 ZEND_PARSE_PARAMETERS_END();
4271
4272 /* Do some initial calculations */
4273 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4274 pad_size_abs = ZEND_ABS(pad_size);
4275 if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) {
4276 zend_argument_value_error(2, "must be less than or equal to 1048576");
4277 RETURN_THROWS();
4278 }
4279
4280 if (input_size >= pad_size_abs) {
4281 /* Copy the original array */
4282 ZVAL_COPY(return_value, input);
4283 return;
4284 }
4285
4286 num_pads = pad_size_abs - input_size;
4287 if (Z_REFCOUNTED_P(pad_value)) {
4288 GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4289 }
4290
4291 array_init_size(return_value, pad_size_abs);
4292 if (HT_FLAGS(Z_ARRVAL_P(input)) & HASH_FLAG_PACKED) {
4293 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4294
4295 if (pad_size < 0) {
4296 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4297 for (i = 0; i < num_pads; i++) {
4298 ZEND_HASH_FILL_ADD(pad_value);
4299 }
4300 } ZEND_HASH_FILL_END();
4301 }
4302
4303 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4304 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4305 Z_TRY_ADDREF_P(value);
4306 ZEND_HASH_FILL_ADD(value);
4307 } ZEND_HASH_FOREACH_END();
4308 } ZEND_HASH_FILL_END();
4309
4310 if (pad_size > 0) {
4311 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4312 for (i = 0; i < num_pads; i++) {
4313 ZEND_HASH_FILL_ADD(pad_value);
4314 }
4315 } ZEND_HASH_FILL_END();
4316 }
4317 } else {
4318 if (pad_size < 0) {
4319 for (i = 0; i < num_pads; i++) {
4320 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4321 }
4322 }
4323
4324 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4325 Z_TRY_ADDREF_P(value);
4326 if (key) {
4327 zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4328 } else {
4329 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4330 }
4331 } ZEND_HASH_FOREACH_END();
4332
4333 if (pad_size > 0) {
4334 for (i = 0; i < num_pads; i++) {
4335 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4336 }
4337 }
4338 }
4339 }
4340 /* }}} */
4341
4342 /* {{{ Return array with key <-> value flipped */
4343 PHP_FUNCTION(array_flip)
4344 {
4345 zval *array, *entry, data;
4346 zend_ulong num_idx;
4347 zend_string *str_idx;
4348
4349 ZEND_PARSE_PARAMETERS_START(1, 1)
4350 Z_PARAM_ARRAY(array)
4351 ZEND_PARSE_PARAMETERS_END();
4352
4353 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4354
4355 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4356 ZVAL_DEREF(entry);
4357 if (Z_TYPE_P(entry) == IS_LONG) {
4358 if (str_idx) {
4359 ZVAL_STR_COPY(&data, str_idx);
4360 } else {
4361 ZVAL_LONG(&data, num_idx);
4362 }
4363 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4364 } else if (Z_TYPE_P(entry) == IS_STRING) {
4365 if (str_idx) {
4366 ZVAL_STR_COPY(&data, str_idx);
4367 } else {
4368 ZVAL_LONG(&data, num_idx);
4369 }
4370 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4371 } else {
4372 php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4373 }
4374 } ZEND_HASH_FOREACH_END();
4375 }
4376 /* }}} */
4377
4378 /* {{{ Returns an array with all string keys lowercased [or uppercased] */
4379 PHP_FUNCTION(array_change_key_case)
4380 {
4381 zval *array, *entry;
4382 zend_string *string_key;
4383 zend_string *new_key;
4384 zend_ulong num_key;
4385 zend_long change_to_upper=0;
4386
4387 ZEND_PARSE_PARAMETERS_START(1, 2)
4388 Z_PARAM_ARRAY(array)
4389 Z_PARAM_OPTIONAL
4390 Z_PARAM_LONG(change_to_upper)
4391 ZEND_PARSE_PARAMETERS_END();
4392
4393 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4394
4395 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4396 if (!string_key) {
4397 entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4398 } else {
4399 if (change_to_upper) {
4400 new_key = php_string_toupper(string_key);
4401 } else {
4402 new_key = php_string_tolower(string_key);
4403 }
4404 entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4405 zend_string_release_ex(new_key, 0);
4406 }
4407
4408 zval_add_ref(entry);
4409 } ZEND_HASH_FOREACH_END();
4410 }
4411 /* }}} */
4412
4413 struct bucketindex {
4414 Bucket b;
4415 unsigned int i;
4416 };
4417
4418 static void array_bucketindex_swap(void *p, void *q)
4419 {
4420 struct bucketindex *f = (struct bucketindex *)p;
4421 struct bucketindex *g = (struct bucketindex *)q;
4422 struct bucketindex t;
4423 t = *f;
4424 *f = *g;
4425 *g = t;
4426 }
4427
4428 /* {{{ Removes duplicate values from array */
4429 PHP_FUNCTION(array_unique)
4430 {
4431 zval *array;
4432 Bucket *p;
4433 zend_long sort_type = PHP_SORT_STRING;
4434 bucket_compare_func_t cmp;
4435 struct bucketindex *arTmp, *cmpdata, *lastkept;
4436 uint32_t i, idx;
4437
4438 ZEND_PARSE_PARAMETERS_START(1, 2)
4439 Z_PARAM_ARRAY(array)
4440 Z_PARAM_OPTIONAL
4441 Z_PARAM_LONG(sort_type)
4442 ZEND_PARSE_PARAMETERS_END();
4443
4444 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4445 ZVAL_COPY(return_value, array);
4446 return;
4447 }
4448
4449 if (sort_type == PHP_SORT_STRING) {
4450 HashTable seen;
4451 zend_long num_key;
4452 zend_string *str_key;
4453 zval *val;
4454
4455 zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4456 array_init(return_value);
4457
4458 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4459 zval *retval;
4460 if (Z_TYPE_P(val) == IS_STRING) {
4461 retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4462 } else {
4463 zend_string *tmp_str_val;
4464 zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4465 retval = zend_hash_add_empty_element(&seen, str_val);
4466 zend_tmp_string_release(tmp_str_val);
4467 }
4468
4469 if (retval) {
4470 /* First occurrence of the value */
4471 if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4472 ZVAL_DEREF(val);
4473 }
4474 Z_TRY_ADDREF_P(val);
4475
4476 if (str_key) {
4477 zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4478 } else {
4479 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4480 }
4481 }
4482 } ZEND_HASH_FOREACH_END();
4483
4484 zend_hash_destroy(&seen);
4485 return;
4486 }
4487
4488 cmp = php_get_data_compare_func_unstable(sort_type, 0);
4489
4490 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4491
4492 /* create and sort array with pointers to the target_hash buckets */
4493 arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4494 for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) {
4495 p = Z_ARRVAL_P(array)->arData + idx;
4496 if (Z_TYPE(p->val) == IS_UNDEF) continue;
4497 arTmp[i].b = *p;
4498 arTmp[i].i = i;
4499 i++;
4500 }
4501 ZVAL_UNDEF(&arTmp[i].b.val);
4502 zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4503 (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4504 /* go through the sorted array and delete duplicates from the copy */
4505 lastkept = arTmp;
4506 for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4507 if (cmp(&lastkept->b, &cmpdata->b)) {
4508 lastkept = cmpdata;
4509 } else {
4510 if (lastkept->i > cmpdata->i) {
4511 p = &lastkept->b;
4512 lastkept = cmpdata;
4513 } else {
4514 p = &cmpdata->b;
4515 }
4516 if (p->key == NULL) {
4517 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4518 } else {
4519 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4520 }
4521 }
4522 }
4523 pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4524 }
4525 /* }}} */
4526
4527 static int zval_compare(zval *first, zval *second) /* {{{ */
4528 {
4529 return string_compare_function(first, second);
4530 }
4531 /* }}} */
4532
4533 static int zval_user_compare(zval *a, zval *b) /* {{{ */
4534 {
4535 zval args[2];
4536 zval retval;
4537
4538 ZVAL_COPY_VALUE(&args[0], a);
4539 ZVAL_COPY_VALUE(&args[1], b);
4540
4541 BG(user_compare_fci).param_count = 2;
4542 BG(user_compare_fci).params = args;
4543 BG(user_compare_fci).retval = &retval;
4544
4545 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4546 zend_long ret = zval_get_long(&retval);
4547 zval_ptr_dtor(&retval);
4548 return ZEND_NORMALIZE_BOOL(ret);
4549 } else {
4550 return 0;
4551 }
4552 }
4553 /* }}} */
4554
4555 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4556 {
4557 int argc, i;
4558 zval *args;
4559 int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4560 bool ok;
4561 zval *val, *data;
4562 char *param_spec;
4563 zend_string *key;
4564 zend_ulong h;
4565
4566 /* Get the argument count */
4567 argc = ZEND_NUM_ARGS();
4568 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4569 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
4570 param_spec = "+f";
4571 intersect_data_compare_func = zval_user_compare;
4572 } else {
4573 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
4574 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
4575 param_spec = "+";
4576
4577 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4578 intersect_data_compare_func = zval_compare;
4579 }
4580 }
4581
4582 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4583 RETURN_THROWS();
4584 }
4585
4586 for (i = 0; i < argc; i++) {
4587 if (Z_TYPE(args[i]) != IS_ARRAY) {
4588 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
4589 RETURN_THROWS();
4590 }
4591 }
4592
4593 array_init(return_value);
4594
4595 /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
4596 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4597 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4598 val = Z_REFVAL_P(val);
4599 }
4600 if (key == NULL) {
4601 ok = 1;
4602 for (i = 1; i < argc; i++) {
4603 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
4604 (intersect_data_compare_func &&
4605 intersect_data_compare_func(val, data) != 0)
4606 ) {
4607 ok = 0;
4608 break;
4609 }
4610 }
4611 if (ok) {
4612 Z_TRY_ADDREF_P(val);
4613 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4614 }
4615 } else {
4616 ok = 1;
4617 for (i = 1; i < argc; i++) {
4618 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
4619 (intersect_data_compare_func &&
4620 intersect_data_compare_func(val, data) != 0)
4621 ) {
4622 ok = 0;
4623 break;
4624 }
4625 }
4626 if (ok) {
4627 Z_TRY_ADDREF_P(val);
4628 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
4629 }
4630 }
4631 } ZEND_HASH_FOREACH_END();
4632 }
4633 /* }}} */
4634
4635 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
4636 {
4637 zval *args = NULL;
4638 HashTable *hash;
4639 int arr_argc, i, c = 0;
4640 uint32_t idx;
4641 Bucket **lists, *list, **ptrs, *p;
4642 char *param_spec;
4643 zend_fcall_info fci1, fci2;
4644 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
4645 zend_fcall_info *fci_key = NULL, *fci_data;
4646 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
4647 PHP_ARRAY_CMP_FUNC_VARS;
4648
4649 bucket_compare_func_t intersect_key_compare_func;
4650 bucket_compare_func_t intersect_data_compare_func;
4651
4652 if (behavior == INTERSECT_NORMAL) {
4653 intersect_key_compare_func = php_array_key_compare_string;
4654
4655 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
4656 /* array_intersect() */
4657 param_spec = "+";
4658 intersect_data_compare_func = php_array_data_compare_string_unstable;
4659 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4660 /* array_uintersect() */
4661 param_spec = "+f";
4662 intersect_data_compare_func = php_array_user_compare_unstable;
4663 } else {
4664 ZEND_ASSERT(0 && "Invalid data_compare_type");
4665 return;
4666 }
4667
4668 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
4669 RETURN_THROWS();
4670 }
4671 fci_data = &fci1;
4672 fci_data_cache = &fci1_cache;
4673
4674 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4675 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
4676 * no comparison of the data is done (part of INTERSECT_ASSOC) */
4677
4678 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
4679 /* array_intersect_assoc() or array_intersect_key() */
4680 param_spec = "+";
4681 intersect_key_compare_func = php_array_key_compare_string_unstable;
4682 intersect_data_compare_func = php_array_data_compare_string_unstable;
4683 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
4684 /* array_uintersect_assoc() */
4685 param_spec = "+f";
4686 intersect_key_compare_func = php_array_key_compare_string_unstable;
4687 intersect_data_compare_func = php_array_user_compare_unstable;
4688 fci_data = &fci1;
4689 fci_data_cache = &fci1_cache;
4690 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
4691 /* array_intersect_uassoc() or array_intersect_ukey() */
4692 param_spec = "+f";
4693 intersect_key_compare_func = php_array_user_key_compare_unstable;
4694 intersect_data_compare_func = php_array_data_compare_string_unstable;
4695 fci_key = &fci1;
4696 fci_key_cache = &fci1_cache;
4697 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
4698 /* array_uintersect_uassoc() */
4699 param_spec = "+ff";
4700 intersect_key_compare_func = php_array_user_key_compare_unstable;
4701 intersect_data_compare_func = php_array_user_compare_unstable;
4702 fci_data = &fci1;
4703 fci_data_cache = &fci1_cache;
4704 fci_key = &fci2;
4705 fci_key_cache = &fci2_cache;
4706 } else {
4707 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
4708 return;
4709 }
4710
4711 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
4712 RETURN_THROWS();
4713 }
4714
4715 } else {
4716 ZEND_ASSERT(0 && "Invalid behavior");
4717 return;
4718 }
4719
4720 PHP_ARRAY_CMP_FUNC_BACKUP();
4721
4722 /* for each argument, create and sort list with pointers to the hash buckets */
4723 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4724 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4725
4726 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
4727 BG(user_compare_fci) = *fci_data;
4728 BG(user_compare_fci_cache) = *fci_data_cache;
4729 } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
4730 BG(user_compare_fci) = *fci_key;
4731 BG(user_compare_fci_cache) = *fci_key_cache;
4732 }
4733
4734 for (i = 0; i < arr_argc; i++) {
4735 if (Z_TYPE(args[i]) != IS_ARRAY) {
4736 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
4737 arr_argc = i; /* only free up to i - 1 */
4738 goto out;
4739 }
4740 hash = Z_ARRVAL(args[i]);
4741 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
4742 lists[i] = list;
4743 ptrs[i] = list;
4744 for (idx = 0; idx < hash->nNumUsed; idx++) {
4745 p = hash->arData + idx;
4746 if (Z_TYPE(p->val) == IS_UNDEF) continue;
4747 *list++ = *p;
4748 }
4749 ZVAL_UNDEF(&list->val);
4750 if (hash->nNumOfElements > 1) {
4751 if (behavior == INTERSECT_NORMAL) {
4752 zend_sort((void *) lists[i], hash->nNumOfElements,
4753 sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
4754 (swap_func_t)zend_hash_bucket_swap);
4755 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4756 zend_sort((void *) lists[i], hash->nNumOfElements,
4757 sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
4758 (swap_func_t)zend_hash_bucket_swap);
4759 }
4760 }
4761 }
4762
4763 /* copy the argument array */
4764 RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
4765
4766 /* go through the lists and look for common values */
4767 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
4768 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
4769 && key_compare_type == INTERSECT_COMP_KEY_USER) {
4770 BG(user_compare_fci) = *fci_key;
4771 BG(user_compare_fci_cache) = *fci_key_cache;
4772 }
4773
4774 for (i = 1; i < arr_argc; i++) {
4775 if (behavior & INTERSECT_NORMAL) {
4776 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
4777 ptrs[i]++;
4778 }
4779 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4780 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
4781 ptrs[i]++;
4782 }
4783 if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
4784 /* this means that ptrs[i] is not NULL so we can compare
4785 * and "c==0" is from last operation
4786 * in this branch of code we enter only when INTERSECT_ASSOC
4787 * since when we have INTERSECT_KEY compare of data is not wanted. */
4788 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4789 BG(user_compare_fci) = *fci_data;
4790 BG(user_compare_fci_cache) = *fci_data_cache;
4791 }
4792 if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
4793 c = 1;
4794 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
4795 BG(user_compare_fci) = *fci_key;
4796 BG(user_compare_fci_cache) = *fci_key_cache;
4797 /* When KEY_USER, the last parameter is always the callback */
4798 }
4799 /* we are going to the break */
4800 } else {
4801 /* continue looping */
4802 }
4803 }
4804 }
4805 if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
4806 /* delete any values corresponding to remains of ptrs[0] */
4807 /* and exit because they do not present in at least one of */
4808 /* the other arguments */
4809 for (;;) {
4810 p = ptrs[0]++;
4811 if (Z_TYPE(p->val) == IS_UNDEF) {
4812 goto out;
4813 }
4814 if (p->key == NULL) {
4815 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4816 } else {
4817 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4818 }
4819 }
4820 }
4821 if (c) /* here we get if not all are equal */
4822 break;
4823 ptrs[i]++;
4824 }
4825 if (c) {
4826 /* Value of ptrs[0] not in all arguments, delete all entries */
4827 /* with value < value of ptrs[i] */
4828 for (;;) {
4829 p = ptrs[0];
4830 if (p->key == NULL) {
4831 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4832 } else {
4833 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4834 }
4835 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4836 goto out;
4837 }
4838 if (behavior == INTERSECT_NORMAL) {
4839 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
4840 break;
4841 }
4842 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4843 /* no need of looping because indexes are unique */
4844 break;
4845 }
4846 }
4847 } else {
4848 /* ptrs[0] is present in all the arguments */
4849 /* Skip all entries with same value as ptrs[0] */
4850 for (;;) {
4851 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4852 goto out;
4853 }
4854 if (behavior == INTERSECT_NORMAL) {
4855 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
4856 break;
4857 }
4858 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4859 /* no need of looping because indexes are unique */
4860 break;
4861 }
4862 }
4863 }
4864 }
4865 out:
4866 for (i = 0; i < arr_argc; i++) {
4867 hash = Z_ARRVAL(args[i]);
4868 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
4869 }
4870
4871 PHP_ARRAY_CMP_FUNC_RESTORE();
4872
4873 efree(ptrs);
4874 efree(lists);
4875 }
4876 /* }}} */
4877
4878 /* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data. */
4879 PHP_FUNCTION(array_intersect_key)
4880 {
4881 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
4882 }
4883 /* }}} */
4884
4885 /* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data. */
4886 PHP_FUNCTION(array_intersect_ukey)
4887 {
4888 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
4889 }
4890 /* }}} */
4891
4892 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
4893 PHP_FUNCTION(array_intersect)
4894 {
4895 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
4896 }
4897 /* }}} */
4898
4899 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */
4900 PHP_FUNCTION(array_uintersect)
4901 {
4902 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
4903 }
4904 /* }}} */
4905
4906 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
4907 PHP_FUNCTION(array_intersect_assoc)
4908 {
4909 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
4910 }
4911 /* }}} */
4912
4913 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using a user-supplied callback. */
4914 PHP_FUNCTION(array_intersect_uassoc)
4915 {
4916 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
4917 }
4918 /* }}} */
4919
4920 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using a user-supplied callback. */
4921 PHP_FUNCTION(array_uintersect_assoc)
4922 {
4923 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
4924 }
4925 /* }}} */
4926
4927 /* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks. */
4928 PHP_FUNCTION(array_uintersect_uassoc)
4929 {
4930 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
4931 }
4932 /* }}} */
4933
4934 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4935 {
4936 int argc, i;
4937 zval *args;
4938 int (*diff_data_compare_func)(zval *, zval *) = NULL;
4939 bool ok;
4940 zval *val, *data;
4941 zend_string *key;
4942 zend_ulong h;
4943
4944 /* Get the argument count */
4945 argc = ZEND_NUM_ARGS();
4946 if (data_compare_type == DIFF_COMP_DATA_USER) {
4947 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4948 RETURN_THROWS();
4949 }
4950 diff_data_compare_func = zval_user_compare;
4951 } else {
4952 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
4953 RETURN_THROWS();
4954 }
4955 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
4956 diff_data_compare_func = zval_compare;
4957 }
4958 }
4959
4960 for (i = 0; i < argc; i++) {
4961 if (Z_TYPE(args[i]) != IS_ARRAY) {
4962 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
4963 RETURN_THROWS();
4964 }
4965 }
4966
4967 array_init(return_value);
4968
4969 /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
4970 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
4971 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4972 val = Z_REFVAL_P(val);
4973 }
4974 if (key == NULL) {
4975 ok = 1;
4976 for (i = 1; i < argc; i++) {
4977 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
4978 (!diff_data_compare_func ||
4979 diff_data_compare_func(val, data) == 0)
4980 ) {
4981 ok = 0;
4982 break;
4983 }
4984 }
4985 if (ok) {
4986 Z_TRY_ADDREF_P(val);
4987 zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
4988 }
4989 } else {
4990 ok = 1;
4991 for (i = 1; i < argc; i++) {
4992 if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
4993 (!diff_data_compare_func ||
4994 diff_data_compare_func(val, data) == 0)
4995 ) {
4996 ok = 0;
4997 break;
4998 }
4999 }
5000 if (ok) {
5001 Z_TRY_ADDREF_P(val);
5002 zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5003 }
5004 }
5005 } ZEND_HASH_FOREACH_END();
5006 }
5007 /* }}} */
5008
5009 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5010 {
5011 zval *args = NULL;
5012 HashTable *hash;
5013 int arr_argc, i, c;
5014 uint32_t idx;
5015 Bucket **lists, *list, **ptrs, *p;
5016 char *param_spec;
5017 zend_fcall_info fci1, fci2;
5018 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5019 zend_fcall_info *fci_key = NULL, *fci_data;
5020 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5021 PHP_ARRAY_CMP_FUNC_VARS;
5022
5023 bucket_compare_func_t diff_key_compare_func;
5024 bucket_compare_func_t diff_data_compare_func;
5025
5026 if (behavior == DIFF_NORMAL) {
5027 diff_key_compare_func = php_array_key_compare_string_unstable;
5028
5029 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5030 /* array_diff */
5031 param_spec = "+";
5032 diff_data_compare_func = php_array_data_compare_string_unstable;
5033 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5034 /* array_udiff */
5035 param_spec = "+f";
5036 diff_data_compare_func = php_array_user_compare_unstable;
5037 } else {
5038 ZEND_ASSERT(0 && "Invalid data_compare_type");
5039 return;
5040 }
5041
5042 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5043 RETURN_THROWS();
5044 }
5045 fci_data = &fci1;
5046 fci_data_cache = &fci1_cache;
5047
5048 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5049 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5050 * no comparison of the data is done (part of DIFF_ASSOC) */
5051
5052 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5053 /* array_diff_assoc() or array_diff_key() */
5054 param_spec = "+";
5055 diff_key_compare_func = php_array_key_compare_string_unstable;
5056 diff_data_compare_func = php_array_data_compare_string_unstable;
5057 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5058 /* array_udiff_assoc() */
5059 param_spec = "+f";
5060 diff_key_compare_func = php_array_key_compare_string_unstable;
5061 diff_data_compare_func = php_array_user_compare_unstable;
5062 fci_data = &fci1;
5063 fci_data_cache = &fci1_cache;
5064 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5065 /* array_diff_uassoc() or array_diff_ukey() */
5066 param_spec = "+f";
5067 diff_key_compare_func = php_array_user_key_compare_unstable;
5068 diff_data_compare_func = php_array_data_compare_string_unstable;
5069 fci_key = &fci1;
5070 fci_key_cache = &fci1_cache;
5071 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5072 /* array_udiff_uassoc() */
5073 param_spec = "+ff";
5074 diff_key_compare_func = php_array_user_key_compare_unstable;
5075 diff_data_compare_func = php_array_user_compare_unstable;
5076 fci_data = &fci1;
5077 fci_data_cache = &fci1_cache;
5078 fci_key = &fci2;
5079 fci_key_cache = &fci2_cache;
5080 } else {
5081 ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5082 return;
5083 }
5084
5085 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5086 RETURN_THROWS();
5087 }
5088
5089 } else {
5090 ZEND_ASSERT(0 && "Invalid behavior");
5091 return;
5092 }
5093
5094 PHP_ARRAY_CMP_FUNC_BACKUP();
5095
5096 /* for each argument, create and sort list with pointers to the hash buckets */
5097 lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5098 ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5099
5100 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5101 BG(user_compare_fci) = *fci_data;
5102 BG(user_compare_fci_cache) = *fci_data_cache;
5103 } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5104 BG(user_compare_fci) = *fci_key;
5105 BG(user_compare_fci_cache) = *fci_key_cache;
5106 }
5107
5108 for (i = 0; i < arr_argc; i++) {
5109 if (Z_TYPE(args[i]) != IS_ARRAY) {
5110 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5111 arr_argc = i; /* only free up to i - 1 */
5112 goto out;
5113 }
5114 hash = Z_ARRVAL(args[i]);
5115 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5116 lists[i] = list;
5117 ptrs[i] = list;
5118 for (idx = 0; idx < hash->nNumUsed; idx++) {
5119 p = hash->arData + idx;
5120 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5121 *list++ = *p;
5122 }
5123 ZVAL_UNDEF(&list->val);
5124 if (hash->nNumOfElements > 1) {
5125 if (behavior == DIFF_NORMAL) {
5126 zend_sort((void *) lists[i], hash->nNumOfElements,
5127 sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5128 (swap_func_t)zend_hash_bucket_swap);
5129 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5130 zend_sort((void *) lists[i], hash->nNumOfElements,
5131 sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5132 (swap_func_t)zend_hash_bucket_swap);
5133 }
5134 }
5135 }
5136
5137 /* copy the argument array */
5138 RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5139
5140 /* go through the lists and look for values of ptr[0] that are not in the others */
5141 while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5142 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5143 &&
5144 key_compare_type == DIFF_COMP_KEY_USER
5145 ) {
5146 BG(user_compare_fci) = *fci_key;
5147 BG(user_compare_fci_cache) = *fci_key_cache;
5148 }
5149 c = 1;
5150 for (i = 1; i < arr_argc; i++) {
5151 Bucket *ptr = ptrs[i];
5152 if (behavior == DIFF_NORMAL) {
5153 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5154 ptrs[i]++;
5155 }
5156 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5157 while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5158 ptr++;
5159 }
5160 }
5161 if (!c) {
5162 if (behavior == DIFF_NORMAL) {
5163 if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5164 ptrs[i]++;
5165 }
5166 break;
5167 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
5168 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5169 * data comparison is not needed - skipped. */
5170 if (Z_TYPE(ptr->val) != IS_UNDEF) {
5171 if (data_compare_type == DIFF_COMP_DATA_USER) {
5172 BG(user_compare_fci) = *fci_data;
5173 BG(user_compare_fci_cache) = *fci_data_cache;
5174 }
5175 if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5176 /* the data is not the same */
5177 c = -1;
5178 if (key_compare_type == DIFF_COMP_KEY_USER) {
5179 BG(user_compare_fci) = *fci_key;
5180 BG(user_compare_fci_cache) = *fci_key_cache;
5181 }
5182 } else {
5183 break;
5184 /* we have found the element in other arrays thus we don't want it
5185 * in the return_value -> delete from there */
5186 }
5187 }
5188 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5189 /* the behavior here differs from INTERSECT_KEY in php_intersect
5190 * since in the "diff" case we have to remove the entry from
5191 * return_value while when doing intersection the entry must not
5192 * be deleted. */
5193 break; /* remove the key */
5194 }
5195 }
5196 }
5197 if (!c) {
5198 /* ptrs[0] in one of the other arguments */
5199 /* delete all entries with value as ptrs[0] */
5200 for (;;) {
5201 p = ptrs[0];
5202 if (p->key == NULL) {
5203 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5204 } else {
5205 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5206 }
5207 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5208 goto out;
5209 }
5210 if (behavior == DIFF_NORMAL) {
5211 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5212 break;
5213 }
5214 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5215 /* in this case no array_key_compare is needed */
5216 break;
5217 }
5218 }
5219 } else {
5220 /* ptrs[0] in none of the other arguments */
5221 /* skip all entries with value as ptrs[0] */
5222 for (;;) {
5223 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5224 goto out;
5225 }
5226 if (behavior == DIFF_NORMAL) {
5227 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5228 break;
5229 }
5230 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5231 /* in this case no array_key_compare is needed */
5232 break;
5233 }
5234 }
5235 }
5236 }
5237 out:
5238 for (i = 0; i < arr_argc; i++) {
5239 hash = Z_ARRVAL(args[i]);
5240 pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5241 }
5242
5243 PHP_ARRAY_CMP_FUNC_RESTORE();
5244
5245 efree(ptrs);
5246 efree(lists);
5247 }
5248 /* }}} */
5249
5250 /* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved. */
5251 PHP_FUNCTION(array_diff_key)
5252 {
5253 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5254 }
5255 /* }}} */
5256
5257 /* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved. */
5258 PHP_FUNCTION(array_diff_ukey)
5259 {
5260 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5261 }
5262 /* }}} */
5263
5264 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5265 PHP_FUNCTION(array_diff)
5266 {
5267 zval *args;
5268 int argc, i;
5269 uint32_t num;
5270 HashTable exclude;
5271 zval *value;
5272 zend_string *str, *tmp_str, *key;
5273 zend_long idx;
5274 zval dummy;
5275
5276 ZEND_PARSE_PARAMETERS_START(1, -1)
5277 Z_PARAM_VARIADIC('+', args, argc)
5278 ZEND_PARSE_PARAMETERS_END();
5279
5280 if (Z_TYPE(args[0]) != IS_ARRAY) {
5281 zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
5282 RETURN_THROWS();
5283 }
5284
5285 num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5286 if (num == 0) {
5287 for (i = 1; i < argc; i++) {
5288 if (Z_TYPE(args[i]) != IS_ARRAY) {
5289 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5290 RETURN_THROWS();
5291 }
5292 }
5293 RETURN_EMPTY_ARRAY();
5294 } else if (num == 1) {
5295 int found = 0;
5296 zend_string *search_str, *tmp_search_str;
5297
5298 value = NULL;
5299 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5300 break;
5301 } ZEND_HASH_FOREACH_END();
5302
5303 if (!value) {
5304 for (i = 1; i < argc; i++) {
5305 if (Z_TYPE(args[i]) != IS_ARRAY) {
5306 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5307 RETURN_THROWS();
5308 }
5309 }
5310 RETURN_EMPTY_ARRAY();
5311 }
5312
5313 search_str = zval_get_tmp_string(value, &tmp_search_str);
5314
5315 for (i = 1; i < argc; i++) {
5316 if (Z_TYPE(args[i]) != IS_ARRAY) {
5317 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5318 RETURN_THROWS();
5319 }
5320 if (!found) {
5321 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5322 str = zval_get_tmp_string(value, &tmp_str);
5323 if (zend_string_equals(search_str, str)) {
5324 zend_tmp_string_release(tmp_str);
5325 found = 1;
5326 break;
5327 }
5328 zend_tmp_string_release(tmp_str);
5329 } ZEND_HASH_FOREACH_END();
5330 }
5331 }
5332
5333 zend_tmp_string_release(tmp_search_str);
5334
5335 if (found) {
5336 RETVAL_EMPTY_ARRAY();
5337 } else {
5338 ZVAL_COPY(return_value, &args[0]);
5339 }
5340 return;
5341 }
5342
5343 /* count number of elements */
5344 num = 0;
5345 for (i = 1; i < argc; i++) {
5346 if (Z_TYPE(args[i]) != IS_ARRAY) {
5347 zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
5348 RETURN_THROWS();
5349 }
5350 num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5351 }
5352
5353 if (num == 0) {
5354 ZVAL_COPY(return_value, &args[0]);
5355 return;
5356 }
5357
5358 ZVAL_NULL(&dummy);
5359 /* create exclude map */
5360 zend_hash_init(&exclude, num, NULL, NULL, 0);
5361 for (i = 1; i < argc; i++) {
5362 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5363 str = zval_get_tmp_string(value, &tmp_str);
5364 zend_hash_add(&exclude, str, &dummy);
5365 zend_tmp_string_release(tmp_str);
5366 } ZEND_HASH_FOREACH_END();
5367 }
5368
5369 /* copy all elements of first array that are not in exclude set */
5370 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5371 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5372 str = zval_get_tmp_string(value, &tmp_str);
5373 if (!zend_hash_exists(&exclude, str)) {
5374 if (key) {
5375 value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5376 } else {
5377 value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5378 }
5379 zval_add_ref(value);
5380 }
5381 zend_tmp_string_release(tmp_str);
5382 } ZEND_HASH_FOREACH_END();
5383
5384 zend_hash_destroy(&exclude);
5385 }
5386 /* }}} */
5387
5388 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function. */
5389 PHP_FUNCTION(array_udiff)
5390 {
5391 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5392 }
5393 /* }}} */
5394
5395 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal */
5396 PHP_FUNCTION(array_diff_assoc)
5397 {
5398 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5399 }
5400 /* }}} */
5401
5402 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function. */
5403 PHP_FUNCTION(array_diff_uassoc)
5404 {
5405 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5406 }
5407 /* }}} */
5408
5409 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function. */
5410 PHP_FUNCTION(array_udiff_assoc)
5411 {
5412 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5413 }
5414 /* }}} */
5415
5416 /* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions. */
5417 PHP_FUNCTION(array_udiff_uassoc)
5418 {
5419 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5420 }
5421 /* }}} */
5422
5423 #define MULTISORT_ORDER 0
5424 #define MULTISORT_TYPE 1
5425 #define MULTISORT_LAST 2
5426
5427 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5428 {
5429 Bucket *ab = *(Bucket **)a;
5430 Bucket *bb = *(Bucket **)b;
5431 int r;
5432 zend_long result;
5433
5434 r = 0;
5435 do {
5436 result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5437 if (result != 0) {
5438 return result > 0 ? 1 : -1;
5439 }
5440 r++;
5441 } while (Z_TYPE(ab[r].val) != IS_UNDEF);
5442
5443 return stable_sort_fallback(&ab[r], &bb[r]);
5444 }
5445 /* }}} */
5446
5447 #define MULTISORT_ABORT \
5448 efree(func); \
5449 efree(arrays); \
5450 return;
5451
5452 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5453 Bucket *t;
5454 Bucket **f = (Bucket **)p;
5455 Bucket **g = (Bucket **)q;
5456
5457 t = *f;
5458 *f = *g;
5459 *g = t;
5460 }
5461 /* }}} */
5462
5463 /* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5464 PHP_FUNCTION(array_multisort)
5465 {
5466 zval* args;
5467 zval** arrays;
5468 Bucket** indirect;
5469 uint32_t idx;
5470 Bucket* p;
5471 HashTable* hash;
5472 int argc;
5473 int array_size;
5474 int num_arrays = 0;
5475 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
5476 int sort_order = PHP_SORT_ASC;
5477 int sort_type = PHP_SORT_REGULAR;
5478 int i, k, n;
5479 bucket_compare_func_t *func;
5480
5481 ZEND_PARSE_PARAMETERS_START(1, -1)
5482 Z_PARAM_VARIADIC('+', args, argc)
5483 ZEND_PARSE_PARAMETERS_END();
5484
5485 /* Allocate space for storing pointers to input arrays and sort flags. */
5486 arrays = (zval **)ecalloc(argc, sizeof(zval *));
5487 for (i = 0; i < MULTISORT_LAST; i++) {
5488 parse_state[i] = 0;
5489 }
5490 func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
5491
5492 /* Here we go through the input arguments and parse them. Each one can
5493 * be either an array or a sort flag which follows an array. If not
5494 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5495 * accordingly. There can't be two sort flags of the same type after an
5496 * array, and the very first argument has to be an array. */
5497 for (i = 0; i < argc; i++) {
5498 zval *arg = &args[i];
5499
5500 ZVAL_DEREF(arg);
5501 if (Z_TYPE_P(arg) == IS_ARRAY) {
5502 SEPARATE_ARRAY(arg);
5503 /* We see the next array, so we update the sort flags of
5504 * the previous array and reset the sort flags. */
5505 if (i > 0) {
5506 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5507 sort_order = PHP_SORT_ASC;
5508 sort_type = PHP_SORT_REGULAR;
5509 }
5510 arrays[num_arrays++] = arg;
5511
5512 /* Next one may be an array or a list of sort flags. */
5513 for (k = 0; k < MULTISORT_LAST; k++) {
5514 parse_state[k] = 1;
5515 }
5516 } else if (Z_TYPE_P(arg) == IS_LONG) {
5517 switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5518 case PHP_SORT_ASC:
5519 case PHP_SORT_DESC:
5520 /* flag allowed here */
5521 if (parse_state[MULTISORT_ORDER] == 1) {
5522 /* Save the flag and make sure then next arg is not the current flag. */
5523 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5524 parse_state[MULTISORT_ORDER] = 0;
5525 } else {
5526 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5527 MULTISORT_ABORT;
5528 }
5529 break;
5530
5531 case PHP_SORT_REGULAR:
5532 case PHP_SORT_NUMERIC:
5533 case PHP_SORT_STRING:
5534 case PHP_SORT_NATURAL:
5535 case PHP_SORT_LOCALE_STRING:
5536 /* flag allowed here */
5537 if (parse_state[MULTISORT_TYPE] == 1) {
5538 /* Save the flag and make sure then next arg is not the current flag. */
5539 sort_type = (int)Z_LVAL_P(arg);
5540 parse_state[MULTISORT_TYPE] = 0;
5541 } else {
5542 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5543 MULTISORT_ABORT;
5544 }
5545 break;
5546
5547 default:
5548 zend_argument_value_error(i + 1, "must be a valid sort flag");
5549 MULTISORT_ABORT;
5550 break;
5551
5552 }
5553 } else {
5554 zend_argument_type_error(i + 1, "must be an array or a sort flag");
5555 MULTISORT_ABORT;
5556 }
5557 }
5558 /* Take care of the last array sort flags. */
5559 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5560
5561 /* Make sure the arrays are of the same size. */
5562 array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
5563 for (i = 0; i < num_arrays; i++) {
5564 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
5565 zend_value_error("Array sizes are inconsistent");
5566 MULTISORT_ABORT;
5567 }
5568 }
5569
5570 /* If all arrays are empty we don't need to do anything. */
5571 if (array_size < 1) {
5572 efree(func);
5573 efree(arrays);
5574 RETURN_TRUE;
5575 }
5576
5577 /* Create the indirection array. This array is of size MxN, where
5578 * M is the number of entries in each input array and N is the number
5579 * of the input arrays + 1. The last column is UNDEF to indicate the end
5580 * of the row. It also stores the original position for stable sorting. */
5581 indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
5582 for (i = 0; i < array_size; i++) {
5583 indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
5584 }
5585 for (i = 0; i < num_arrays; i++) {
5586 k = 0;
5587 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
5588 p = Z_ARRVAL_P(arrays[i])->arData + idx;
5589 if (Z_TYPE(p->val) == IS_UNDEF) continue;
5590 indirect[k][i] = *p;
5591 k++;
5592 }
5593 }
5594 for (k = 0; k < array_size; k++) {
5595 ZVAL_UNDEF(&indirect[k][num_arrays].val);
5596 Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
5597 }
5598
5599 /* Do the actual sort magic - bada-bim, bada-boom. */
5600 zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
5601 if (EG(exception)) {
5602 goto clean_up;
5603 }
5604
5605 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
5606 for (i = 0; i < num_arrays; i++) {
5607 int repack;
5608
5609 hash = Z_ARRVAL_P(arrays[i]);
5610 hash->nNumUsed = array_size;
5611 hash->nInternalPointer = 0;
5612 repack = !(HT_FLAGS(hash) & HASH_FLAG_PACKED);
5613
5614 for (n = 0, k = 0; k < array_size; k++) {
5615 hash->arData[k] = indirect[k][i];
5616 if (hash->arData[k].key == NULL) {
5617 hash->arData[k].h = n++;
5618 } else {
5619 repack = 0;
5620 }
5621 }
5622 hash->nNextFreeElement = array_size;
5623 if (repack) {
5624 zend_hash_to_packed(hash);
5625 } else if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
5626 zend_hash_rehash(hash);
5627 }
5628 }
5629 RETVAL_TRUE;
5630
5631 clean_up:
5632 for (i = 0; i < array_size; i++) {
5633 efree(indirect[i]);
5634 }
5635 efree(indirect);
5636 efree(func);
5637 efree(arrays);
5638 }
5639 /* }}} */
5640
5641 /* {{{ Return key/keys for random entry/entries in the array */
5642 PHP_FUNCTION(array_rand)
5643 {
5644 zval *input;
5645 zend_long num_req = 1;
5646 zend_string *string_key;
5647 zend_ulong num_key;
5648 int i;
5649 int num_avail;
5650 zend_bitset bitset;
5651 int negative_bitset = 0;
5652 uint32_t bitset_len;
5653 ALLOCA_FLAG(use_heap)
5654
5655 ZEND_PARSE_PARAMETERS_START(1, 2)
5656 Z_PARAM_ARRAY(input)
5657 Z_PARAM_OPTIONAL
5658 Z_PARAM_LONG(num_req)
5659 ZEND_PARSE_PARAMETERS_END();
5660
5661 num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
5662
5663 if (num_avail == 0) {
5664 zend_argument_value_error(1, "cannot be empty");
5665 RETURN_THROWS();
5666 }
5667
5668 if (num_req == 1) {
5669 HashTable *ht = Z_ARRVAL_P(input);
5670
5671 if ((uint32_t)num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) {
5672 /* If less than 1/2 of elements are used, don't sample. Instead search for a
5673 * specific offset using linear scan. */
5674 zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1);
5675 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
5676 if (i == randval) {
5677 if (string_key) {
5678 RETURN_STR_COPY(string_key);
5679 } else {
5680 RETURN_LONG(num_key);
5681 }
5682 }
5683 i++;
5684 } ZEND_HASH_FOREACH_END();
5685 }
5686
5687 /* Sample random buckets until we hit one that is not empty.
5688 * The worst case probability of hitting an empty element is 1-1/2. The worst case
5689 * probability of hitting N empty elements in a row is (1-1/2)**N.
5690 * For N=10 this becomes smaller than 0.1%. */
5691 do {
5692 zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
5693 Bucket *bucket = &ht->arData[randval];
5694 if (!Z_ISUNDEF(bucket->val)) {
5695 if (bucket->key) {
5696 RETURN_STR_COPY(bucket->key);
5697 } else {
5698 RETURN_LONG(bucket->h);
5699 }
5700 }
5701 } while (1);
5702 }
5703
5704 if (num_req <= 0 || num_req > num_avail) {
5705 zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
5706 RETURN_THROWS();
5707 }
5708
5709 /* Make the return value an array only if we need to pass back more than one result. */
5710 array_init_size(return_value, (uint32_t)num_req);
5711 if (num_req > (num_avail >> 1)) {
5712 negative_bitset = 1;
5713 num_req = num_avail - num_req;
5714 }
5715
5716 bitset_len = zend_bitset_len(num_avail);
5717 bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
5718 zend_bitset_clear(bitset, bitset_len);
5719
5720 i = num_req;
5721 while (i) {
5722 zend_long randval = php_mt_rand_range(0, num_avail - 1);
5723 if (!zend_bitset_in(bitset, randval)) {
5724 zend_bitset_incl(bitset, randval);
5725 i--;
5726 }
5727 }
5728 /* i = 0; */
5729
5730 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
5731 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
5732 /* We can't use zend_hash_index_find()
5733 * because the array may have string keys or gaps. */
5734 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
5735 if (zend_bitset_in(bitset, i) ^ negative_bitset) {
5736 if (string_key) {
5737 ZEND_HASH_FILL_SET_STR_COPY(string_key);
5738 } else {
5739 ZEND_HASH_FILL_SET_LONG(num_key);
5740 }
5741 ZEND_HASH_FILL_NEXT();
5742 }
5743 i++;
5744 } ZEND_HASH_FOREACH_END();
5745 } ZEND_HASH_FILL_END();
5746
5747 free_alloca(bitset, use_heap);
5748 }
5749 /* }}} */
5750
5751 /* {{{ Returns the sum of the array entries */
5752 PHP_FUNCTION(array_sum)
5753 {
5754 zval *input,
5755 *entry,
5756 entry_n;
5757
5758 ZEND_PARSE_PARAMETERS_START(1, 1)
5759 Z_PARAM_ARRAY(input)
5760 ZEND_PARSE_PARAMETERS_END();
5761
5762 ZVAL_LONG(return_value, 0);
5763
5764 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
5765 if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
5766 continue;
5767 }
5768 ZVAL_COPY(&entry_n, entry);
5769 convert_scalar_to_number(&entry_n);
5770 fast_add_function(return_value, return_value, &entry_n);
5771 } ZEND_HASH_FOREACH_END();
5772 }
5773 /* }}} */
5774
5775 /* {{{ Returns the product of the array entries */
5776 PHP_FUNCTION(array_product)
5777 {
5778 zval *input,
5779 *entry,
5780 entry_n;
5781 double dval;
5782
5783 ZEND_PARSE_PARAMETERS_START(1, 1)
5784 Z_PARAM_ARRAY(input)
5785 ZEND_PARSE_PARAMETERS_END();
5786
5787 ZVAL_LONG(return_value, 1);
5788 if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
5789 return;
5790 }
5791
5792 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
5793 if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
5794 continue;
5795 }
5796 ZVAL_COPY(&entry_n, entry);
5797 convert_scalar_to_number(&entry_n);
5798
5799 if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
5800 dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
5801 if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
5802 Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
5803 continue;
5804 }
5805 }
5806 convert_to_double(return_value);
5807 convert_to_double(&entry_n);
5808 Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
5809 } ZEND_HASH_FOREACH_END();
5810 }
5811 /* }}} */
5812
5813 /* {{{ Iteratively reduce the array to a single value via the callback. */
5814 PHP_FUNCTION(array_reduce)
5815 {
5816 zval *input;
5817 zval args[2];
5818 zval *operand;
5819 zval retval;
5820 zend_fcall_info fci;
5821 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5822 zval *initial = NULL;
5823 HashTable *htbl;
5824
5825 ZEND_PARSE_PARAMETERS_START(2, 3)
5826 Z_PARAM_ARRAY(input)
5827 Z_PARAM_FUNC(fci, fci_cache)
5828 Z_PARAM_OPTIONAL
5829 Z_PARAM_ZVAL(initial)
5830 ZEND_PARSE_PARAMETERS_END();
5831
5832
5833 if (ZEND_NUM_ARGS() > 2) {
5834 ZVAL_COPY(return_value, initial);
5835 } else {
5836 ZVAL_NULL(return_value);
5837 }
5838
5839 /* (zval **)input points to an element of argument stack
5840 * the base pointer of which is subject to change.
5841 * thus we need to keep the pointer to the hashtable for safety */
5842 htbl = Z_ARRVAL_P(input);
5843
5844 if (zend_hash_num_elements(htbl) == 0) {
5845 return;
5846 }
5847
5848 fci.retval = &retval;
5849 fci.param_count = 2;
5850
5851 ZEND_HASH_FOREACH_VAL(htbl, operand) {
5852 ZVAL_COPY_VALUE(&args[0], return_value);
5853 ZVAL_COPY(&args[1], operand);
5854 fci.params = args;
5855
5856 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
5857 zval_ptr_dtor(&args[1]);
5858 zval_ptr_dtor(&args[0]);
5859 ZVAL_COPY_VALUE(return_value, &retval);
5860 if (UNEXPECTED(Z_ISREF_P(return_value))) {
5861 zend_unwrap_reference(return_value);
5862 }
5863 } else {
5864 zval_ptr_dtor(&args[1]);
5865 zval_ptr_dtor(&args[0]);
5866 RETURN_NULL();
5867 }
5868 } ZEND_HASH_FOREACH_END();
5869 }
5870 /* }}} */
5871
5872 /* {{{ Filters elements from the array via the callback. */
5873 PHP_FUNCTION(array_filter)
5874 {
5875 zval *array;
5876 zval *operand;
5877 zval *key;
5878 zval args[2];
5879 zval retval;
5880 bool have_callback = 0;
5881 zend_long use_type = 0;
5882 zend_string *string_key;
5883 zend_fcall_info fci = empty_fcall_info;
5884 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5885 zend_ulong num_key;
5886
5887 ZEND_PARSE_PARAMETERS_START(1, 3)
5888 Z_PARAM_ARRAY(array)
5889 Z_PARAM_OPTIONAL
5890 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
5891 Z_PARAM_LONG(use_type)
5892 ZEND_PARSE_PARAMETERS_END();
5893
5894 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
5895 RETVAL_EMPTY_ARRAY();
5896 return;
5897 }
5898 array_init(return_value);
5899
5900 if (ZEND_FCI_INITIALIZED(fci)) {
5901 have_callback = 1;
5902 fci.retval = &retval;
5903 if (use_type == ARRAY_FILTER_USE_BOTH) {
5904 fci.param_count = 2;
5905 key = &args[1];
5906 } else {
5907 fci.param_count = 1;
5908 key = &args[0];
5909 }
5910 }
5911
5912 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
5913 if (have_callback) {
5914 if (use_type) {
5915 /* Set up the key */
5916 if (!string_key) {
5917 ZVAL_LONG(key, num_key);
5918 } else {
5919 ZVAL_STR_COPY(key, string_key);
5920 }
5921 }
5922 if (use_type != ARRAY_FILTER_USE_KEY) {
5923 ZVAL_COPY(&args[0], operand);
5924 }
5925 fci.params = args;
5926
5927 if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
5928 int retval_true;
5929
5930 zval_ptr_dtor(&args[0]);
5931 if (use_type == ARRAY_FILTER_USE_BOTH) {
5932 zval_ptr_dtor(&args[1]);
5933 }
5934 retval_true = zend_is_true(&retval);
5935 zval_ptr_dtor(&retval);
5936 if (!retval_true) {
5937 continue;
5938 }
5939 } else {
5940 zval_ptr_dtor(&args[0]);
5941 if (use_type == ARRAY_FILTER_USE_BOTH) {
5942 zval_ptr_dtor(&args[1]);
5943 }
5944 return;
5945 }
5946 } else if (!zend_is_true(operand)) {
5947 continue;
5948 }
5949
5950 if (string_key) {
5951 operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
5952 } else {
5953 operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
5954 }
5955 zval_add_ref(operand);
5956 } ZEND_HASH_FOREACH_END();
5957 }
5958 /* }}} */
5959
5960 /* {{{ Applies the callback to the elements in given arrays. */
5961 PHP_FUNCTION(array_map)
5962 {
5963 zval *arrays = NULL;
5964 int n_arrays = 0;
5965 zval result;
5966 zend_fcall_info fci = empty_fcall_info;
5967 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5968 int i;
5969 uint32_t k, maxlen = 0;
5970
5971 ZEND_PARSE_PARAMETERS_START(2, -1)
5972 Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
5973 Z_PARAM_VARIADIC('+', arrays, n_arrays)
5974 ZEND_PARSE_PARAMETERS_END();
5975
5976 if (n_arrays == 1) {
5977 zend_ulong num_key;
5978 zend_string *str_key;
5979 zval *zv, arg;
5980 int ret;
5981
5982 if (Z_TYPE(arrays[0]) != IS_ARRAY) {
5983 zend_argument_type_error(2, "must be of type array, %s given", zend_zval_type_name(&arrays[0]));
5984 RETURN_THROWS();
5985 }
5986 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
5987
5988 /* Short-circuit: if no callback and only one array, just return it. */
5989 if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
5990 ZVAL_COPY(return_value, &arrays[0]);
5991 return;
5992 }
5993
5994 array_init_size(return_value, maxlen);
5995 zend_hash_real_init(Z_ARRVAL_P(return_value), HT_FLAGS(Z_ARRVAL(arrays[0])) & HASH_FLAG_PACKED);
5996
5997 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
5998 fci.retval = &result;
5999 fci.param_count = 1;
6000 fci.params = &arg;
6001
6002 ZVAL_COPY(&arg, zv);
6003 ret = zend_call_function(&fci, &fci_cache);
6004 i_zval_ptr_dtor(&arg);
6005 if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6006 zend_array_destroy(Z_ARR_P(return_value));
6007 RETURN_NULL();
6008 }
6009 if (str_key) {
6010 _zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
6011 } else {
6012 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
6013 }
6014 } ZEND_HASH_FOREACH_END();
6015 } else {
6016 uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6017
6018 for (i = 0; i < n_arrays; i++) {
6019 if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6020 zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_type_name(&arrays[i]));
6021 efree(array_pos);
6022 RETURN_THROWS();
6023 }
6024 if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6025 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6026 }
6027 }
6028
6029 array_init_size(return_value, maxlen);
6030
6031 if (!ZEND_FCI_INITIALIZED(fci)) {
6032 zval zv;
6033
6034 /* We iterate through all the arrays at once. */
6035 for (k = 0; k < maxlen; k++) {
6036
6037 /* If no callback, the result will be an array, consisting of current
6038 * entries from all arrays. */
6039 array_init_size(&result, n_arrays);
6040
6041 for (i = 0; i < n_arrays; i++) {
6042 /* If this array still has elements, add the current one to the
6043 * parameter list, otherwise use null value. */
6044 uint32_t pos = array_pos[i];
6045 while (1) {
6046 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6047 ZVAL_NULL(&zv);
6048 break;
6049 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6050 ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6051 array_pos[i] = pos + 1;
6052 break;
6053 }
6054 pos++;
6055 }
6056
6057 zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6058 }
6059
6060 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6061 }
6062 } else {
6063 zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6064
6065 /* We iterate through all the arrays at once. */
6066 for (k = 0; k < maxlen; k++) {
6067 for (i = 0; i < n_arrays; i++) {
6068 /* If this array still has elements, add the current one to the
6069 * parameter list, otherwise use null value. */
6070 uint32_t pos = array_pos[i];
6071 while (1) {
6072 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6073 ZVAL_NULL(¶ms[i]);
6074 break;
6075 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6076 ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6077 array_pos[i] = pos + 1;
6078 break;
6079 }
6080 pos++;
6081 }
6082 }
6083
6084 fci.retval = &result;
6085 fci.param_count = n_arrays;
6086 fci.params = params;
6087
6088 if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
6089 efree(array_pos);
6090 zend_array_destroy(Z_ARR_P(return_value));
6091 for (i = 0; i < n_arrays; i++) {
6092 zval_ptr_dtor(¶ms[i]);
6093 }
6094 efree(params);
6095 RETURN_NULL();
6096 } else {
6097 for (i = 0; i < n_arrays; i++) {
6098 zval_ptr_dtor(¶ms[i]);
6099 }
6100 }
6101
6102 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6103 }
6104
6105 efree(params);
6106 }
6107 efree(array_pos);
6108 }
6109 }
6110 /* }}} */
6111
6112 /* {{{ Checks if the given key or index exists in the array */
6113 PHP_FUNCTION(array_key_exists)
6114 {
6115 zval *key;
6116 HashTable *ht;
6117
6118 ZEND_PARSE_PARAMETERS_START(2, 2)
6119 Z_PARAM_ZVAL(key)
6120 Z_PARAM_ARRAY_HT(ht)
6121 ZEND_PARSE_PARAMETERS_END();
6122
6123 switch (Z_TYPE_P(key)) {
6124 case IS_STRING:
6125 RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6126 break;
6127 case IS_LONG:
6128 RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6129 break;
6130 case IS_NULL:
6131 RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6132 break;
6133 case IS_DOUBLE:
6134 RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6135 break;
6136 case IS_FALSE:
6137 RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6138 break;
6139 case IS_TRUE:
6140 RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6141 break;
6142 case IS_RESOURCE:
6143 zend_use_resource_as_offset(key);
6144 RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6145 break;
6146 default:
6147 zend_argument_type_error(1, "must be a valid array offset type");
6148 break;
6149 }
6150 }
6151 /* }}} */
6152
6153 /* {{{ Split array into chunks */
6154 PHP_FUNCTION(array_chunk)
6155 {
6156 int num_in;
6157 zend_long size, current = 0;
6158 zend_string *str_key;
6159 zend_ulong num_key;
6160 bool preserve_keys = 0;
6161 zval *input = NULL;
6162 zval chunk;
6163 zval *entry;
6164
6165 ZEND_PARSE_PARAMETERS_START(2, 3)
6166 Z_PARAM_ARRAY(input)
6167 Z_PARAM_LONG(size)
6168 Z_PARAM_OPTIONAL
6169 Z_PARAM_BOOL(preserve_keys)
6170 ZEND_PARSE_PARAMETERS_END();
6171
6172 /* Do bounds checking for size parameter. */
6173 if (size < 1) {
6174 zend_argument_value_error(2, "must be greater than 0");
6175 RETURN_THROWS();
6176 }
6177
6178 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6179
6180 if (size > num_in) {
6181 if (num_in == 0) {
6182 RETVAL_EMPTY_ARRAY();
6183 return;
6184 }
6185 size = num_in;
6186 }
6187
6188 array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6189
6190 ZVAL_UNDEF(&chunk);
6191
6192 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6193 /* If new chunk, create and initialize it. */
6194 if (Z_TYPE(chunk) == IS_UNDEF) {
6195 array_init_size(&chunk, (uint32_t)size);
6196 }
6197
6198 /* Add entry to the chunk, preserving keys if necessary. */
6199 if (preserve_keys) {
6200 if (str_key) {
6201 entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6202 } else {
6203 entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6204 }
6205 } else {
6206 entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6207 }
6208 zval_add_ref(entry);
6209
6210 /* If reached the chunk size, add it to the result array, and reset the
6211 * pointer. */
6212 if (!(++current % size)) {
6213 add_next_index_zval(return_value, &chunk);
6214 ZVAL_UNDEF(&chunk);
6215 }
6216 } ZEND_HASH_FOREACH_END();
6217
6218 /* Add the final chunk if there is one. */
6219 if (Z_TYPE(chunk) != IS_UNDEF) {
6220 add_next_index_zval(return_value, &chunk);
6221 }
6222 }
6223 /* }}} */
6224
6225 /* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6226 PHP_FUNCTION(array_combine)
6227 {
6228 HashTable *values, *keys;
6229 uint32_t pos_values = 0;
6230 zval *entry_keys, *entry_values;
6231 int num_keys, num_values;
6232
6233 ZEND_PARSE_PARAMETERS_START(2, 2)
6234 Z_PARAM_ARRAY_HT(keys)
6235 Z_PARAM_ARRAY_HT(values)
6236 ZEND_PARSE_PARAMETERS_END();
6237
6238 num_keys = zend_hash_num_elements(keys);
6239 num_values = zend_hash_num_elements(values);
6240
6241 if (num_keys != num_values) {
6242 zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6243 RETURN_THROWS();
6244 }
6245
6246 if (!num_keys) {
6247 RETURN_EMPTY_ARRAY();
6248 }
6249
6250 array_init_size(return_value, num_keys);
6251 ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6252 while (1) {
6253 if (pos_values >= values->nNumUsed) {
6254 break;
6255 } else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) {
6256 entry_values = &values->arData[pos_values].val;
6257 if (Z_TYPE_P(entry_keys) == IS_LONG) {
6258 entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6259 Z_LVAL_P(entry_keys), entry_values);
6260 } else {
6261 zend_string *tmp_key;
6262 zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6263 entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6264 key, entry_values);
6265 zend_tmp_string_release(tmp_key);
6266 }
6267 zval_add_ref(entry_values);
6268 pos_values++;
6269 break;
6270 }
6271 pos_values++;
6272 }
6273 } ZEND_HASH_FOREACH_END();
6274 }
6275 /* }}} */
6276