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