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