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