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