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