1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 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_smart_str.h"
49 #ifdef HAVE_SPL
50 #include "ext/spl/spl_array.h"
51 #endif
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 COUNT_NORMAL 0
68 #define COUNT_RECURSIVE 1
69
70 #define DIFF_NORMAL 1
71 #define DIFF_KEY 2
72 #define DIFF_ASSOC 6
73 #define DIFF_COMP_DATA_NONE -1
74 #define DIFF_COMP_DATA_INTERNAL 0
75 #define DIFF_COMP_DATA_USER 1
76 #define DIFF_COMP_KEY_INTERNAL 0
77 #define DIFF_COMP_KEY_USER 1
78
79 #define INTERSECT_NORMAL 1
80 #define INTERSECT_KEY 2
81 #define INTERSECT_ASSOC 6
82 #define INTERSECT_COMP_DATA_NONE -1
83 #define INTERSECT_COMP_DATA_INTERNAL 0
84 #define INTERSECT_COMP_DATA_USER 1
85 #define INTERSECT_COMP_KEY_INTERNAL 0
86 #define INTERSECT_COMP_KEY_USER 1
87
88 #define DOUBLE_DRIFT_FIX 0.000000000000001
89 /* }}} */
90
ZEND_DECLARE_MODULE_GLOBALS(array)91 ZEND_DECLARE_MODULE_GLOBALS(array)
92
93 /* {{{ php_array_init_globals
94 */
95 static void php_array_init_globals(zend_array_globals *array_globals)
96 {
97 memset(array_globals, 0, sizeof(zend_array_globals));
98 }
99 /* }}} */
100
PHP_MINIT_FUNCTION(array)101 PHP_MINIT_FUNCTION(array) /* {{{ */
102 {
103 ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
104
105 REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
106 REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
107 REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
108 REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
109 REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
110 REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
111 REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
112 REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
113
114 REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
115 REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
116
117 REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
118 REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
119 REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
120 REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
121
122 REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
123 REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
124
125 REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
126 REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, 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_set_compare_func(int sort_type TSRMLS_DC)142 static void php_set_compare_func(int sort_type TSRMLS_DC) /* {{{ */
143 {
144 switch (sort_type) {
145 case PHP_SORT_NUMERIC:
146 ARRAYG(compare_func) = numeric_compare_function;
147 break;
148
149 case PHP_SORT_STRING:
150 ARRAYG(compare_func) = string_compare_function;
151 break;
152
153 #if HAVE_STRCOLL
154 case PHP_SORT_LOCALE_STRING:
155 ARRAYG(compare_func) = string_locale_compare_function;
156 break;
157 #endif
158
159 case PHP_SORT_REGULAR:
160 default:
161 ARRAYG(compare_func) = compare_function;
162 break;
163 }
164 }
165 /* }}} */
166
php_array_key_compare(const void * a,const void * b TSRMLS_DC)167 static int php_array_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
168 {
169 Bucket *f;
170 Bucket *s;
171 zval result;
172 zval first;
173 zval second;
174
175 f = *((Bucket **) a);
176 s = *((Bucket **) b);
177
178 if (f->nKeyLength == 0) {
179 Z_TYPE(first) = IS_LONG;
180 Z_LVAL(first) = f->h;
181 } else {
182 Z_TYPE(first) = IS_STRING;
183 Z_STRVAL(first) = f->arKey;
184 Z_STRLEN(first) = f->nKeyLength - 1;
185 }
186
187 if (s->nKeyLength == 0) {
188 Z_TYPE(second) = IS_LONG;
189 Z_LVAL(second) = s->h;
190 } else {
191 Z_TYPE(second) = IS_STRING;
192 Z_STRVAL(second) = s->arKey;
193 Z_STRLEN(second) = s->nKeyLength - 1;
194 }
195
196 if (ARRAYG(compare_func)(&result, &first, &second TSRMLS_CC) == FAILURE) {
197 return 0;
198 }
199
200 if (Z_TYPE(result) == IS_DOUBLE) {
201 if (Z_DVAL(result) < 0) {
202 return -1;
203 } else if (Z_DVAL(result) > 0) {
204 return 1;
205 } else {
206 return 0;
207 }
208 }
209
210 convert_to_long(&result);
211
212 if (Z_LVAL(result) < 0) {
213 return -1;
214 } else if (Z_LVAL(result) > 0) {
215 return 1;
216 }
217
218 return 0;
219 }
220 /* }}} */
221
php_array_reverse_key_compare(const void * a,const void * b TSRMLS_DC)222 static int php_array_reverse_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
223 {
224 return php_array_key_compare(a, b TSRMLS_CC) * -1;
225 }
226 /* }}} */
227
228 /* {{{ proto bool krsort(array &array_arg [, int sort_flags])
229 Sort an array by key value in reverse order */
PHP_FUNCTION(krsort)230 PHP_FUNCTION(krsort)
231 {
232 zval *array;
233 long sort_type = PHP_SORT_REGULAR;
234
235 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
236 RETURN_FALSE;
237 }
238
239 php_set_compare_func(sort_type TSRMLS_CC);
240
241 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_key_compare, 0 TSRMLS_CC) == FAILURE) {
242 RETURN_FALSE;
243 }
244 RETURN_TRUE;
245 }
246 /* }}} */
247
248 /* {{{ proto bool ksort(array &array_arg [, int sort_flags])
249 Sort an array by key */
PHP_FUNCTION(ksort)250 PHP_FUNCTION(ksort)
251 {
252 zval *array;
253 long sort_type = PHP_SORT_REGULAR;
254
255 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
256 RETURN_FALSE;
257 }
258
259 php_set_compare_func(sort_type TSRMLS_CC);
260
261 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_key_compare, 0 TSRMLS_CC) == FAILURE) {
262 RETURN_FALSE;
263 }
264 RETURN_TRUE;
265 }
266 /* }}} */
267
php_count_recursive(zval * array,long mode TSRMLS_DC)268 static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
269 {
270 long cnt = 0;
271 zval **element;
272
273 if (Z_TYPE_P(array) == IS_ARRAY) {
274 if (Z_ARRVAL_P(array)->nApplyCount > 1) {
275 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
276 return 0;
277 }
278
279 cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
280 if (mode == COUNT_RECURSIVE) {
281 HashPosition pos;
282
283 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
284 zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
285 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
286 ) {
287 Z_ARRVAL_P(array)->nApplyCount++;
288 cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
289 Z_ARRVAL_P(array)->nApplyCount--;
290 }
291 }
292 }
293
294 return cnt;
295 }
296 /* }}} */
297
298 /* {{{ proto int count(mixed var [, int mode])
299 Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)300 PHP_FUNCTION(count)
301 {
302 zval *array;
303 long mode = COUNT_NORMAL;
304
305 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
306 return;
307 }
308
309 switch (Z_TYPE_P(array)) {
310 case IS_NULL:
311 RETURN_LONG(0);
312 break;
313 case IS_ARRAY:
314 RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
315 break;
316 case IS_OBJECT: {
317 #ifdef HAVE_SPL
318 zval *retval;
319 #endif
320 /* first, we check if the handler is defined */
321 if (Z_OBJ_HT_P(array)->count_elements) {
322 RETVAL_LONG(1);
323 if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value) TSRMLS_CC)) {
324 return;
325 }
326 }
327 #ifdef HAVE_SPL
328 /* if not and the object implements Countable we call its count() method */
329 if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
330 zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval);
331 if (retval) {
332 convert_to_long_ex(&retval);
333 RETVAL_LONG(Z_LVAL_P(retval));
334 zval_ptr_dtor(&retval);
335 }
336 return;
337 }
338 #endif
339 }
340 default:
341 RETURN_LONG(1);
342 break;
343 }
344 }
345 /* }}} */
346
347 /* Numbers are always smaller than strings int this function as it
348 * anyway doesn't make much sense to compare two different data types.
349 * This keeps it consistant and simple.
350 *
351 * This is not correct any more, depends on what compare_func is set to.
352 */
php_array_data_compare(const void * a,const void * b TSRMLS_DC)353 static int php_array_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
354 {
355 Bucket *f;
356 Bucket *s;
357 zval result;
358 zval *first;
359 zval *second;
360
361 f = *((Bucket **) a);
362 s = *((Bucket **) b);
363
364 first = *((zval **) f->pData);
365 second = *((zval **) s->pData);
366
367 if (ARRAYG(compare_func)(&result, first, second TSRMLS_CC) == FAILURE) {
368 return 0;
369 }
370
371 if (Z_TYPE(result) == IS_DOUBLE) {
372 if (Z_DVAL(result) < 0) {
373 return -1;
374 } else if (Z_DVAL(result) > 0) {
375 return 1;
376 } else {
377 return 0;
378 }
379 }
380
381 convert_to_long(&result);
382
383 if (Z_LVAL(result) < 0) {
384 return -1;
385 } else if (Z_LVAL(result) > 0) {
386 return 1;
387 }
388
389 return 0;
390 }
391 /* }}} */
392
php_array_reverse_data_compare(const void * a,const void * b TSRMLS_DC)393 static int php_array_reverse_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
394 {
395 return php_array_data_compare(a, b TSRMLS_CC) * -1;
396 }
397 /* }}} */
398
php_array_natural_general_compare(const void * a,const void * b,int fold_case)399 static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */
400 {
401 Bucket *f, *s;
402 zval *fval, *sval;
403 zval first, second;
404 int result;
405
406 f = *((Bucket **) a);
407 s = *((Bucket **) b);
408
409 fval = *((zval **) f->pData);
410 sval = *((zval **) s->pData);
411 first = *fval;
412 second = *sval;
413
414 if (Z_TYPE_P(fval) != IS_STRING) {
415 zval_copy_ctor(&first);
416 convert_to_string(&first);
417 }
418
419 if (Z_TYPE_P(sval) != IS_STRING) {
420 zval_copy_ctor(&second);
421 convert_to_string(&second);
422 }
423
424 result = strnatcmp_ex(Z_STRVAL(first), Z_STRLEN(first), Z_STRVAL(second), Z_STRLEN(second), fold_case);
425
426 if (Z_TYPE_P(fval) != IS_STRING) {
427 zval_dtor(&first);
428 }
429
430 if (Z_TYPE_P(sval) != IS_STRING) {
431 zval_dtor(&second);
432 }
433
434 return result;
435 }
436 /* }}} */
437
php_array_natural_compare(const void * a,const void * b TSRMLS_DC)438 static int php_array_natural_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
439 {
440 return php_array_natural_general_compare(a, b, 0);
441 }
442 /* }}} */
443
php_array_natural_case_compare(const void * a,const void * b TSRMLS_DC)444 static int php_array_natural_case_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
445 {
446 return php_array_natural_general_compare(a, b, 1);
447 }
448 /* }}} */
449
php_natsort(INTERNAL_FUNCTION_PARAMETERS,int fold_case)450 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
451 {
452 zval *array;
453
454 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
455 return;
456 }
457
458 if (fold_case) {
459 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_case_compare, 0 TSRMLS_CC) == FAILURE) {
460 return;
461 }
462 } else {
463 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_compare, 0 TSRMLS_CC) == FAILURE) {
464 return;
465 }
466 }
467
468 RETURN_TRUE;
469 }
470 /* }}} */
471
472 /* {{{ proto void natsort(array &array_arg)
473 Sort an array using natural sort */
PHP_FUNCTION(natsort)474 PHP_FUNCTION(natsort)
475 {
476 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
477 }
478 /* }}} */
479
480 /* {{{ proto void natcasesort(array &array_arg)
481 Sort an array using case-insensitive natural sort */
PHP_FUNCTION(natcasesort)482 PHP_FUNCTION(natcasesort)
483 {
484 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
485 }
486 /* }}} */
487
488 /* {{{ proto bool asort(array &array_arg [, int sort_flags])
489 Sort an array and maintain index association */
PHP_FUNCTION(asort)490 PHP_FUNCTION(asort)
491 {
492 zval *array;
493 long sort_type = PHP_SORT_REGULAR;
494
495 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
496 RETURN_FALSE;
497 }
498
499 php_set_compare_func(sort_type TSRMLS_CC);
500
501 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 0 TSRMLS_CC) == FAILURE) {
502 RETURN_FALSE;
503 }
504 RETURN_TRUE;
505 }
506 /* }}} */
507
508 /* {{{ proto bool arsort(array &array_arg [, int sort_flags])
509 Sort an array in reverse order and maintain index association */
PHP_FUNCTION(arsort)510 PHP_FUNCTION(arsort)
511 {
512 zval *array;
513 long sort_type = PHP_SORT_REGULAR;
514
515 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
516 RETURN_FALSE;
517 }
518
519 php_set_compare_func(sort_type TSRMLS_CC);
520
521 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 0 TSRMLS_CC) == FAILURE) {
522 RETURN_FALSE;
523 }
524 RETURN_TRUE;
525 }
526 /* }}} */
527
528 /* {{{ proto bool sort(array &array_arg [, int sort_flags])
529 Sort an array */
PHP_FUNCTION(sort)530 PHP_FUNCTION(sort)
531 {
532 zval *array;
533 long sort_type = PHP_SORT_REGULAR;
534
535 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
536 RETURN_FALSE;
537 }
538
539 php_set_compare_func(sort_type TSRMLS_CC);
540
541 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 1 TSRMLS_CC) == FAILURE) {
542 RETURN_FALSE;
543 }
544 RETURN_TRUE;
545 }
546 /* }}} */
547
548 /* {{{ proto bool rsort(array &array_arg [, int sort_flags])
549 Sort an array in reverse order */
PHP_FUNCTION(rsort)550 PHP_FUNCTION(rsort)
551 {
552 zval *array;
553 long sort_type = PHP_SORT_REGULAR;
554
555 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
556 RETURN_FALSE;
557 }
558
559 php_set_compare_func(sort_type TSRMLS_CC);
560
561 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 1 TSRMLS_CC) == FAILURE) {
562 RETURN_FALSE;
563 }
564 RETURN_TRUE;
565 }
566 /* }}} */
567
php_array_user_compare(const void * a,const void * b TSRMLS_DC)568 static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
569 {
570 Bucket *f;
571 Bucket *s;
572 zval **args[2];
573 zval *retval_ptr = NULL;
574
575 f = *((Bucket **) a);
576 s = *((Bucket **) b);
577
578 args[0] = (zval **) f->pData;
579 args[1] = (zval **) s->pData;
580
581 BG(user_compare_fci).param_count = 2;
582 BG(user_compare_fci).params = args;
583 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
584 BG(user_compare_fci).no_separation = 0;
585 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
586 long retval;
587
588 convert_to_long_ex(&retval_ptr);
589 retval = Z_LVAL_P(retval_ptr);
590 zval_ptr_dtor(&retval_ptr);
591 return retval < 0 ? -1 : retval > 0 ? 1 : 0;
592 } else {
593 return 0;
594 }
595 }
596 /* }}} */
597
598 /* check if comparison function is valid */
599 #define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
600 if (!zend_is_callable(*func_name, 0, NULL TSRMLS_CC)) { \
601 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid comparison function"); \
602 BG(user_compare_fci) = old_user_compare_fci; \
603 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
604 RETURN_FALSE; \
605 } \
606
607 /* Clear FCI cache otherwise : for example the same or other array with
608 * (partly) the same key values has been sorted with uasort() or
609 * other sorting function the comparison is cached, however the name
610 * of the function for comparison is not respected. see bug #28739 AND #33295
611 *
612 * Following defines will assist in backup / restore values. */
613
614 #define PHP_ARRAY_CMP_FUNC_VARS \
615 zend_fcall_info old_user_compare_fci; \
616 zend_fcall_info_cache old_user_compare_fci_cache \
617
618 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
619 old_user_compare_fci = BG(user_compare_fci); \
620 old_user_compare_fci_cache = BG(user_compare_fci_cache); \
621 BG(user_compare_fci_cache) = empty_fcall_info_cache; \
622
623 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
624 BG(user_compare_fci) = old_user_compare_fci; \
625 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
626
627 /* {{{ proto bool usort(array array_arg, string cmp_function)
628 Sort an array by values using a user-defined comparison function */
PHP_FUNCTION(usort)629 PHP_FUNCTION(usort)
630 {
631 zval *array;
632 int refcount;
633 PHP_ARRAY_CMP_FUNC_VARS;
634
635 PHP_ARRAY_CMP_FUNC_BACKUP();
636
637 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
638 PHP_ARRAY_CMP_FUNC_RESTORE();
639 return;
640 }
641
642 /* Clear the is_ref flag, so the attemts to modify the array in user
643 * comparison function will create a copy of array and won't affect the
644 * original array. The fact of modification is detected using refcount
645 * comparison. The result of sorting in such case is undefined and the
646 * function returns FALSE.
647 */
648 Z_UNSET_ISREF_P(array);
649 refcount = Z_REFCOUNT_P(array);
650
651 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 1 TSRMLS_CC) == FAILURE) {
652 RETVAL_FALSE;
653 } else {
654 if (refcount > Z_REFCOUNT_P(array)) {
655 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
656 RETVAL_FALSE;
657 } else {
658 RETVAL_TRUE;
659 }
660 }
661
662 if (Z_REFCOUNT_P(array) > 1) {
663 Z_SET_ISREF_P(array);
664 }
665
666 PHP_ARRAY_CMP_FUNC_RESTORE();
667 }
668 /* }}} */
669
670 /* {{{ proto bool uasort(array array_arg, string cmp_function)
671 Sort an array with a user-defined comparison function and maintain index association */
PHP_FUNCTION(uasort)672 PHP_FUNCTION(uasort)
673 {
674 zval *array;
675 int refcount;
676 PHP_ARRAY_CMP_FUNC_VARS;
677
678 PHP_ARRAY_CMP_FUNC_BACKUP();
679
680 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
681 PHP_ARRAY_CMP_FUNC_RESTORE();
682 return;
683 }
684
685 /* Clear the is_ref flag, so the attemts to modify the array in user
686 * comaprison function will create a copy of array and won't affect the
687 * original array. The fact of modification is detected using refcount
688 * comparison. The result of sorting in such case is undefined and the
689 * function returns FALSE.
690 */
691 Z_UNSET_ISREF_P(array);
692 refcount = Z_REFCOUNT_P(array);
693
694 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 0 TSRMLS_CC) == FAILURE) {
695 RETVAL_FALSE;
696 } else {
697 if (refcount > Z_REFCOUNT_P(array)) {
698 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
699 RETVAL_FALSE;
700 } else {
701 RETVAL_TRUE;
702 }
703 }
704
705 if (Z_REFCOUNT_P(array) > 1) {
706 Z_SET_ISREF_P(array);
707 }
708
709 PHP_ARRAY_CMP_FUNC_RESTORE();
710 }
711 /* }}} */
712
php_array_user_key_compare(const void * a,const void * b TSRMLS_DC)713 static int php_array_user_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
714 {
715 Bucket *f;
716 Bucket *s;
717 zval *key1, *key2;
718 zval **args[2];
719 zval *retval_ptr = NULL;
720 long result;
721
722 ALLOC_INIT_ZVAL(key1);
723 ALLOC_INIT_ZVAL(key2);
724 args[0] = &key1;
725 args[1] = &key2;
726
727 f = *((Bucket **) a);
728 s = *((Bucket **) b);
729
730 if (f->nKeyLength == 0) {
731 Z_LVAL_P(key1) = f->h;
732 Z_TYPE_P(key1) = IS_LONG;
733 } else {
734 Z_STRVAL_P(key1) = estrndup(f->arKey, f->nKeyLength - 1);
735 Z_STRLEN_P(key1) = f->nKeyLength - 1;
736 Z_TYPE_P(key1) = IS_STRING;
737 }
738 if (s->nKeyLength == 0) {
739 Z_LVAL_P(key2) = s->h;
740 Z_TYPE_P(key2) = IS_LONG;
741 } else {
742 Z_STRVAL_P(key2) = estrndup(s->arKey, s->nKeyLength - 1);
743 Z_STRLEN_P(key2) = s->nKeyLength - 1;
744 Z_TYPE_P(key2) = IS_STRING;
745 }
746
747 BG(user_compare_fci).param_count = 2;
748 BG(user_compare_fci).params = args;
749 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
750 BG(user_compare_fci).no_separation = 0;
751 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
752 convert_to_long_ex(&retval_ptr);
753 result = Z_LVAL_P(retval_ptr);
754 zval_ptr_dtor(&retval_ptr);
755 } else {
756 result = 0;
757 }
758
759 zval_ptr_dtor(&key1);
760 zval_ptr_dtor(&key2);
761
762 return result;
763 }
764 /* }}} */
765
766 /* {{{ proto bool uksort(array array_arg, string cmp_function)
767 Sort an array by keys using a user-defined comparison function */
PHP_FUNCTION(uksort)768 PHP_FUNCTION(uksort)
769 {
770 zval *array;
771 int refcount;
772 PHP_ARRAY_CMP_FUNC_VARS;
773
774 PHP_ARRAY_CMP_FUNC_BACKUP();
775
776 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
777 PHP_ARRAY_CMP_FUNC_RESTORE();
778 return;
779 }
780
781 /* Clear the is_ref flag, so the attemts to modify the array in user
782 * comaprison function will create a copy of array and won't affect the
783 * original array. The fact of modification is detected using refcount
784 * comparison. The result of sorting in such case is undefined and the
785 * function returns FALSE.
786 */
787 Z_UNSET_ISREF_P(array);
788 refcount = Z_REFCOUNT_P(array);
789
790 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_key_compare, 0 TSRMLS_CC) == FAILURE) {
791 RETVAL_FALSE;
792 } else {
793 if (refcount > Z_REFCOUNT_P(array)) {
794 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
795 RETVAL_FALSE;
796 } else {
797 RETVAL_TRUE;
798 }
799 }
800
801 if (Z_REFCOUNT_P(array) > 1) {
802 Z_SET_ISREF_P(array);
803 }
804
805 PHP_ARRAY_CMP_FUNC_RESTORE();
806 }
807 /* }}} */
808
809 /* {{{ proto mixed end(array array_arg)
810 Advances array argument's internal pointer to the last element and return it */
PHP_FUNCTION(end)811 PHP_FUNCTION(end)
812 {
813 HashTable *array;
814 zval **entry;
815
816 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
817 return;
818 }
819
820 zend_hash_internal_pointer_end(array);
821
822 if (return_value_used) {
823 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
824 RETURN_FALSE;
825 }
826
827 RETURN_ZVAL(*entry, 1, 0);
828 }
829 }
830 /* }}} */
831
832 /* {{{ proto mixed prev(array array_arg)
833 Move array argument's internal pointer to the previous element and return it */
PHP_FUNCTION(prev)834 PHP_FUNCTION(prev)
835 {
836 HashTable *array;
837 zval **entry;
838
839 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
840 return;
841 }
842
843 zend_hash_move_backwards(array);
844
845 if (return_value_used) {
846 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
847 RETURN_FALSE;
848 }
849
850 RETURN_ZVAL(*entry, 1, 0);
851 }
852 }
853 /* }}} */
854
855 /* {{{ proto mixed next(array array_arg)
856 Move array argument's internal pointer to the next element and return it */
PHP_FUNCTION(next)857 PHP_FUNCTION(next)
858 {
859 HashTable *array;
860 zval **entry;
861
862 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
863 return;
864 }
865
866 zend_hash_move_forward(array);
867
868 if (return_value_used) {
869 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
870 RETURN_FALSE;
871 }
872
873 RETURN_ZVAL(*entry, 1, 0);
874 }
875 }
876 /* }}} */
877
878 /* {{{ proto mixed reset(array array_arg)
879 Set array argument's internal pointer to the first element and return it */
PHP_FUNCTION(reset)880 PHP_FUNCTION(reset)
881 {
882 HashTable *array;
883 zval **entry;
884
885 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
886 return;
887 }
888
889 zend_hash_internal_pointer_reset(array);
890
891 if (return_value_used) {
892 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
893 RETURN_FALSE;
894 }
895
896 RETURN_ZVAL(*entry, 1, 0);
897 }
898 }
899 /* }}} */
900
901 /* {{{ proto mixed current(array array_arg)
902 Return the element currently pointed to by the internal array pointer */
PHP_FUNCTION(current)903 PHP_FUNCTION(current)
904 {
905 HashTable *array;
906 zval **entry;
907
908 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
909 return;
910 }
911
912 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
913 RETURN_FALSE;
914 }
915 RETURN_ZVAL(*entry, 1, 0);
916 }
917 /* }}} */
918
919 /* {{{ proto mixed key(array array_arg)
920 Return the key of the element currently pointed to by the internal array pointer */
PHP_FUNCTION(key)921 PHP_FUNCTION(key)
922 {
923 HashTable *array;
924 char *string_key;
925 uint string_length;
926 ulong num_key;
927
928 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
929 return;
930 }
931
932 switch (zend_hash_get_current_key_ex(array, &string_key, &string_length, &num_key, 0, NULL)) {
933 case HASH_KEY_IS_STRING:
934 RETVAL_STRINGL(string_key, string_length - 1, 1);
935 break;
936 case HASH_KEY_IS_LONG:
937 RETVAL_LONG(num_key);
938 break;
939 case HASH_KEY_NON_EXISTANT:
940 return;
941 }
942 }
943 /* }}} */
944
945 /* {{{ proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
946 Return the lowest value in an array or a series of arguments */
PHP_FUNCTION(min)947 PHP_FUNCTION(min)
948 {
949 int argc;
950 zval ***args = NULL;
951
952 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
953 return;
954 }
955
956 php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
957
958 /* mixed min ( array $values ) */
959 if (argc == 1) {
960 zval **result;
961
962 if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
963 php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
964 RETVAL_NULL();
965 } else {
966 if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) {
967 RETVAL_ZVAL(*result, 1, 0);
968 } else {
969 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
970 RETVAL_FALSE;
971 }
972 }
973 } else {
974 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
975 zval **min, result;
976 int i;
977
978 min = args[0];
979
980 for (i = 1; i < argc; i++) {
981 is_smaller_function(&result, *args[i], *min TSRMLS_CC);
982 if (Z_LVAL(result) == 1) {
983 min = args[i];
984 }
985 }
986
987 RETVAL_ZVAL(*min, 1, 0);
988 }
989
990 if (args) {
991 efree(args);
992 }
993 }
994 /* }}} */
995
996 /* {{{ proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
997 Return the highest value in an array or a series of arguments */
PHP_FUNCTION(max)998 PHP_FUNCTION(max)
999 {
1000 zval ***args = NULL;
1001 int argc;
1002
1003 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
1004 return;
1005 }
1006
1007 php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
1008
1009 /* mixed max ( array $values ) */
1010 if (argc == 1) {
1011 zval **result;
1012
1013 if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
1014 php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
1015 RETVAL_NULL();
1016 } else {
1017 if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) {
1018 RETVAL_ZVAL(*result, 1, 0);
1019 } else {
1020 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
1021 RETVAL_FALSE;
1022 }
1023 }
1024 } else {
1025 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1026 zval **max, result;
1027 int i;
1028
1029 max = args[0];
1030
1031 for (i = 1; i < argc; i++) {
1032 is_smaller_or_equal_function(&result, *args[i], *max TSRMLS_CC);
1033 if (Z_LVAL(result) == 0) {
1034 max = args[i];
1035 }
1036 }
1037
1038 RETVAL_ZVAL(*max, 1, 0);
1039 }
1040
1041 if (args) {
1042 efree(args);
1043 }
1044 }
1045 /* }}} */
1046
php_array_walk(HashTable * target_hash,zval * userdata,int recursive TSRMLS_DC)1047 static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive TSRMLS_DC) /* {{{ */
1048 {
1049 zval **args[3], /* Arguments to userland function */
1050 *retval_ptr, /* Return value - unused */
1051 *key=NULL; /* Entry key */
1052 char *string_key;
1053 uint string_key_len;
1054 ulong num_key;
1055
1056 /* Set up known arguments */
1057 args[1] = &key;
1058 args[2] = &userdata;
1059 if (userdata) {
1060 Z_ADDREF_P(userdata);
1061 }
1062
1063 BG(array_walk_fci).retval_ptr_ptr = &retval_ptr;
1064 BG(array_walk_fci).param_count = userdata ? 3 : 2;
1065 BG(array_walk_fci).params = args;
1066 BG(array_walk_fci).no_separation = 0;
1067
1068 /* Iterate through hash */
1069 zend_hash_internal_pointer_reset(target_hash);
1070 while (!EG(exception) && zend_hash_get_current_data(target_hash, (void **)&args[0]) == SUCCESS) {
1071 if (recursive && Z_TYPE_PP(args[0]) == IS_ARRAY) {
1072 HashTable *thash;
1073 zend_fcall_info orig_array_walk_fci;
1074 zend_fcall_info_cache orig_array_walk_fci_cache;
1075
1076 SEPARATE_ZVAL_IF_NOT_REF(args[0]);
1077 thash = Z_ARRVAL_PP(args[0]);
1078 if (thash->nApplyCount > 1) {
1079 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
1080 if (userdata) {
1081 zval_ptr_dtor(&userdata);
1082 }
1083 return 0;
1084 }
1085
1086 /* backup the fcall info and cache */
1087 orig_array_walk_fci = BG(array_walk_fci);
1088 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1089
1090 thash->nApplyCount++;
1091 php_array_walk(thash, userdata, recursive TSRMLS_CC);
1092 thash->nApplyCount--;
1093
1094 /* restore the fcall info and cache */
1095 BG(array_walk_fci) = orig_array_walk_fci;
1096 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1097 } else {
1098 /* Allocate space for key */
1099 MAKE_STD_ZVAL(key);
1100
1101 /* Set up the key */
1102 switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_key_len, &num_key, 0, NULL)) {
1103 case HASH_KEY_IS_LONG:
1104 Z_TYPE_P(key) = IS_LONG;
1105 Z_LVAL_P(key) = num_key;
1106 break;
1107 case HASH_KEY_IS_STRING:
1108 ZVAL_STRINGL(key, string_key, string_key_len - 1, 1);
1109 break;
1110 }
1111
1112 /* Call the userland function */
1113 if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache) TSRMLS_CC) == SUCCESS) {
1114 if (retval_ptr) {
1115 zval_ptr_dtor(&retval_ptr);
1116 }
1117 } else {
1118 if (key) {
1119 zval_ptr_dtor(&key);
1120 key = NULL;
1121 }
1122 break;
1123 }
1124 }
1125
1126 if (key) {
1127 zval_ptr_dtor(&key);
1128 key = NULL;
1129 }
1130 zend_hash_move_forward(target_hash);
1131 }
1132
1133 if (userdata) {
1134 zval_ptr_dtor(&userdata);
1135 }
1136 return 0;
1137 }
1138 /* }}} */
1139
1140 /* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
1141 Apply a user function to every member of an array */
PHP_FUNCTION(array_walk)1142 PHP_FUNCTION(array_walk)
1143 {
1144 HashTable *array;
1145 zval *userdata = NULL;
1146 zend_fcall_info orig_array_walk_fci;
1147 zend_fcall_info_cache orig_array_walk_fci_cache;
1148
1149 orig_array_walk_fci = BG(array_walk_fci);
1150 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1151
1152 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1153 BG(array_walk_fci) = orig_array_walk_fci;
1154 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1155 return;
1156 }
1157
1158 php_array_walk(array, userdata, 0 TSRMLS_CC);
1159 BG(array_walk_fci) = orig_array_walk_fci;
1160 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1161 RETURN_TRUE;
1162 }
1163 /* }}} */
1164
1165 /* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
1166 Apply a user function recursively to every member of an array */
PHP_FUNCTION(array_walk_recursive)1167 PHP_FUNCTION(array_walk_recursive)
1168 {
1169 HashTable *array;
1170 zval *userdata = NULL;
1171 zend_fcall_info orig_array_walk_fci;
1172 zend_fcall_info_cache orig_array_walk_fci_cache;
1173
1174 orig_array_walk_fci = BG(array_walk_fci);
1175 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1176
1177 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1178 BG(array_walk_fci) = orig_array_walk_fci;
1179 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1180 return;
1181 }
1182
1183 php_array_walk(array, userdata, 1 TSRMLS_CC);
1184 BG(array_walk_fci) = orig_array_walk_fci;
1185 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1186 RETURN_TRUE;
1187 }
1188 /* }}} */
1189
1190 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1191 * 0 = return boolean
1192 * 1 = return key
1193 */
php_search_array(INTERNAL_FUNCTION_PARAMETERS,int behavior)1194 static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1195 {
1196 zval *value, /* value to check for */
1197 *array, /* array to check in */
1198 **entry, /* pointer to array entry */
1199 res; /* comparison result */
1200 HashPosition pos; /* hash iterator */
1201 zend_bool strict = 0; /* strict comparison or not */
1202 ulong num_key;
1203 uint str_key_len;
1204 char *string_key;
1205 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
1206
1207 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) {
1208 return;
1209 }
1210
1211 if (strict) {
1212 is_equal_func = is_identical_function;
1213 }
1214
1215 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
1216 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
1217 is_equal_func(&res, value, *entry TSRMLS_CC);
1218 if (Z_LVAL(res)) {
1219 if (behavior == 0) {
1220 RETURN_TRUE;
1221 } else {
1222 /* Return current key */
1223 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
1224 case HASH_KEY_IS_STRING:
1225 RETURN_STRINGL(string_key, str_key_len - 1, 1);
1226 break;
1227 case HASH_KEY_IS_LONG:
1228 RETURN_LONG(num_key);
1229 break;
1230 }
1231 }
1232 }
1233 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
1234 }
1235
1236 RETURN_FALSE;
1237 }
1238 /* }}} */
1239
1240 /* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
1241 Checks if the given value exists in the array */
PHP_FUNCTION(in_array)1242 PHP_FUNCTION(in_array)
1243 {
1244 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1245 }
1246 /* }}} */
1247
1248 /* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
1249 Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)1250 PHP_FUNCTION(array_search)
1251 {
1252 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1253 }
1254 /* }}} */
1255
php_valid_var_name(char * var_name,int var_name_len)1256 static int php_valid_var_name(char *var_name, int var_name_len) /* {{{ */
1257 {
1258 int i, ch;
1259
1260 if (!var_name || !var_name_len) {
1261 return 0;
1262 }
1263
1264 /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1265 ch = (int)((unsigned char *)var_name)[0];
1266 if (var_name[0] != '_' &&
1267 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1268 (ch < 97 /* a */ || /* z */ ch > 122) &&
1269 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1270 ) {
1271 return 0;
1272 }
1273
1274 /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1275 if (var_name_len > 1) {
1276 for (i = 1; i < var_name_len; i++) {
1277 ch = (int)((unsigned char *)var_name)[i];
1278 if (var_name[i] != '_' &&
1279 (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
1280 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1281 (ch < 97 /* a */ || /* z */ ch > 122) &&
1282 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1283 ) {
1284 return 0;
1285 }
1286 }
1287 }
1288 return 1;
1289 }
1290 /* }}} */
1291
php_prefix_varname(zval * result,zval * prefix,char * var_name,int var_name_len,zend_bool add_underscore TSRMLS_DC)1292 PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, int var_name_len, zend_bool add_underscore TSRMLS_DC) /* {{{ */
1293 {
1294 Z_STRLEN_P(result) = Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len;
1295 Z_TYPE_P(result) = IS_STRING;
1296 Z_STRVAL_P(result) = emalloc(Z_STRLEN_P(result) + 1);
1297 memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
1298
1299 if (add_underscore) {
1300 Z_STRVAL_P(result)[Z_STRLEN_P(prefix)] = '_';
1301 }
1302
1303 memcpy(Z_STRVAL_P(result) + Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1304
1305 return SUCCESS;
1306 }
1307 /* }}} */
1308
1309 /* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
1310 Imports variables into symbol table from an array */
PHP_FUNCTION(extract)1311 PHP_FUNCTION(extract)
1312 {
1313 zval *var_array, *prefix = NULL;
1314 long extract_type = EXTR_OVERWRITE;
1315 zval **entry, *data;
1316 char *var_name;
1317 ulong num_key;
1318 uint var_name_len;
1319 int var_exists, key_type, count = 0;
1320 int extract_refs = 0;
1321 HashPosition pos;
1322
1323 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) {
1324 return;
1325 }
1326
1327 extract_refs = (extract_type & EXTR_REFS);
1328 extract_type &= 0xff;
1329
1330 if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
1331 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid extract type");
1332 return;
1333 }
1334
1335 if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
1336 php_error_docref(NULL TSRMLS_CC, E_WARNING, "specified extract type requires the prefix parameter");
1337 return;
1338 }
1339
1340 if (prefix) {
1341 convert_to_string(prefix);
1342 if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) {
1343 php_error_docref(NULL TSRMLS_CC, E_WARNING, "prefix is not a valid identifier");
1344 return;
1345 }
1346 }
1347
1348 if (!EG(active_symbol_table)) {
1349 zend_rebuild_symbol_table(TSRMLS_C);
1350 }
1351
1352 /* var_array is passed by ref for the needs of EXTR_REFS (needs to
1353 * work on the original array to create refs to its members)
1354 * simulate pass_by_value if EXTR_REFS is not used */
1355 if (!extract_refs) {
1356 SEPARATE_ARG_IF_REF(var_array);
1357 }
1358
1359 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos);
1360 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&entry, &pos) == SUCCESS) {
1361 zval final_name;
1362
1363 ZVAL_NULL(&final_name);
1364
1365 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &var_name, &var_name_len, &num_key, 0, &pos);
1366 var_exists = 0;
1367
1368 if (key_type == HASH_KEY_IS_STRING) {
1369 var_name_len--;
1370 var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
1371 } else if (key_type == HASH_KEY_IS_LONG && (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID)) {
1372 zval num;
1373
1374 ZVAL_LONG(&num, num_key);
1375 convert_to_string(&num);
1376 php_prefix_varname(&final_name, prefix, Z_STRVAL(num), Z_STRLEN(num), 1 TSRMLS_CC);
1377 zval_dtor(&num);
1378 } else {
1379 zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
1380 continue;
1381 }
1382
1383 switch (extract_type) {
1384 case EXTR_IF_EXISTS:
1385 if (!var_exists) break;
1386 /* break omitted intentionally */
1387
1388 case EXTR_OVERWRITE:
1389 /* GLOBALS protection */
1390 if (var_exists && var_name_len == sizeof("GLOBALS")-1 && !strcmp(var_name, "GLOBALS")) {
1391 break;
1392 }
1393 if (var_exists && var_name_len == sizeof("this")-1 && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) {
1394 break;
1395 }
1396 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1397 break;
1398
1399 case EXTR_PREFIX_IF_EXISTS:
1400 if (var_exists) {
1401 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1402 }
1403 break;
1404
1405 case EXTR_PREFIX_SAME:
1406 if (!var_exists && var_name_len != 0) {
1407 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1408 }
1409 /* break omitted intentionally */
1410
1411 case EXTR_PREFIX_ALL:
1412 if (Z_TYPE(final_name) == IS_NULL && var_name_len != 0) {
1413 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1414 }
1415 break;
1416
1417 case EXTR_PREFIX_INVALID:
1418 if (Z_TYPE(final_name) == IS_NULL) {
1419 if (!php_valid_var_name(var_name, var_name_len)) {
1420 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1421 } else {
1422 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1423 }
1424 }
1425 break;
1426
1427 default:
1428 if (!var_exists) {
1429 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1430 }
1431 break;
1432 }
1433
1434 if (Z_TYPE(final_name) != IS_NULL && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1435 if (extract_refs) {
1436 zval **orig_var;
1437
1438 SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
1439 zval_add_ref(entry);
1440
1441 if (zend_hash_find(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) &orig_var) == SUCCESS) {
1442 zval_ptr_dtor(orig_var);
1443 *orig_var = *entry;
1444 } else {
1445 zend_hash_update(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) entry, sizeof(zval *), NULL);
1446 }
1447 } else {
1448 MAKE_STD_ZVAL(data);
1449 *data = **entry;
1450 zval_copy_ctor(data);
1451
1452 ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
1453 }
1454 count++;
1455 }
1456 zval_dtor(&final_name);
1457
1458 zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
1459 }
1460
1461 if (!extract_refs) {
1462 zval_ptr_dtor(&var_array);
1463 }
1464
1465 RETURN_LONG(count);
1466 }
1467 /* }}} */
1468
php_compact_var(HashTable * eg_active_symbol_table,zval * return_value,zval * entry TSRMLS_DC)1469 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry TSRMLS_DC) /* {{{ */
1470 {
1471 zval **value_ptr, *value, *data;
1472
1473 if (Z_TYPE_P(entry) == IS_STRING) {
1474 if (zend_hash_find(eg_active_symbol_table, Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, (void **)&value_ptr) != FAILURE) {
1475 value = *value_ptr;
1476 ALLOC_ZVAL(data);
1477 MAKE_COPY_ZVAL(&value, data);
1478
1479 zend_hash_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, &data, sizeof(zval *), NULL);
1480 }
1481 }
1482 else if (Z_TYPE_P(entry) == IS_ARRAY) {
1483 HashPosition pos;
1484
1485 if ((Z_ARRVAL_P(entry)->nApplyCount > 1)) {
1486 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
1487 return;
1488 }
1489
1490 Z_ARRVAL_P(entry)->nApplyCount++;
1491
1492 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
1493 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), (void**)&value_ptr, &pos) == SUCCESS) {
1494 value = *value_ptr;
1495
1496 php_compact_var(eg_active_symbol_table, return_value, value TSRMLS_CC);
1497 zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos);
1498 }
1499 Z_ARRVAL_P(entry)->nApplyCount--;
1500 }
1501 }
1502 /* }}} */
1503
1504 /* {{{ proto array compact(mixed var_names [, mixed ...])
1505 Creates a hash containing variables and their values */
PHP_FUNCTION(compact)1506 PHP_FUNCTION(compact)
1507 {
1508 zval ***args = NULL; /* function arguments array */
1509 int num_args, i;
1510
1511 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1512 return;
1513 }
1514
1515 if (!EG(active_symbol_table)) {
1516 zend_rebuild_symbol_table(TSRMLS_C);
1517 }
1518
1519 /* compact() is probably most used with a single array of var_names
1520 or multiple string names, rather than a combination of both.
1521 So quickly guess a minimum result size based on that */
1522 if (ZEND_NUM_ARGS() == 1 && Z_TYPE_PP(args[0]) == IS_ARRAY) {
1523 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(args[0])));
1524 } else {
1525 array_init_size(return_value, ZEND_NUM_ARGS());
1526 }
1527
1528 for (i=0; i<ZEND_NUM_ARGS(); i++) {
1529 php_compact_var(EG(active_symbol_table), return_value, *args[i] TSRMLS_CC);
1530 }
1531
1532 if (args) {
1533 efree(args);
1534 }
1535 }
1536 /* }}} */
1537
1538 /* {{{ proto array array_fill(int start_key, int num, mixed val)
1539 Create an array containing num elements starting with index start_key each initialized to val */
PHP_FUNCTION(array_fill)1540 PHP_FUNCTION(array_fill)
1541 {
1542 zval *val;
1543 long start_key, num;
1544
1545 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llz", &start_key, &num, &val) == FAILURE) {
1546 return;
1547 }
1548
1549 if (num < 1) {
1550 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements must be positive");
1551 RETURN_FALSE;
1552 }
1553
1554 /* allocate an array for return */
1555 array_init_size(return_value, num);
1556
1557 num--;
1558 zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, &val, sizeof(zval *), NULL);
1559 zval_add_ref(&val);
1560
1561 while (num--) {
1562 if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val, sizeof(zval *), NULL) == SUCCESS) {
1563 zval_add_ref(&val);
1564 } else {
1565 zval_dtor(return_value);
1566 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
1567 RETURN_FALSE;
1568 }
1569 }
1570 }
1571 /* }}} */
1572
1573 /* {{{ proto array array_fill_keys(array keys, mixed val)
1574 Create an array using the elements of the first parameter as keys each initialized to val */
PHP_FUNCTION(array_fill_keys)1575 PHP_FUNCTION(array_fill_keys)
1576 {
1577 zval *keys, *val, **entry;
1578 HashPosition pos;
1579
1580 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &keys, &val) == FAILURE) {
1581 return;
1582 }
1583
1584 /* Initialize return array */
1585 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
1586
1587 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
1588 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry, &pos) == SUCCESS) {
1589
1590 if (Z_TYPE_PP(entry) == IS_LONG) {
1591 zval_add_ref(&val);
1592 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &val, sizeof(zval *), NULL);
1593 } else {
1594 zval key, *key_ptr = *entry;
1595
1596 if (Z_TYPE_PP(entry) != IS_STRING) {
1597 key = **entry;
1598 zval_copy_ctor(&key);
1599 convert_to_string(&key);
1600 key_ptr = &key;
1601 }
1602
1603 zval_add_ref(&val);
1604 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, &val, sizeof(zval *), NULL);
1605
1606 if (key_ptr != *entry) {
1607 zval_dtor(&key);
1608 }
1609 }
1610
1611 zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
1612 }
1613 }
1614 /* }}} */
1615
1616 /* {{{ proto array range(mixed low, mixed high[, int step])
1617 Create an array containing the range of integers or characters from low to high (inclusive) */
PHP_FUNCTION(range)1618 PHP_FUNCTION(range)
1619 {
1620 zval *zlow, *zhigh, *zstep = NULL;
1621 int err = 0, is_step_double = 0;
1622 double step = 1.0;
1623
1624 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) {
1625 RETURN_FALSE;
1626 }
1627
1628 if (zstep) {
1629 if (Z_TYPE_P(zstep) == IS_DOUBLE ||
1630 (Z_TYPE_P(zstep) == IS_STRING && is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0) == IS_DOUBLE)
1631 ) {
1632 is_step_double = 1;
1633 }
1634
1635 convert_to_double_ex(&zstep);
1636 step = Z_DVAL_P(zstep);
1637
1638 /* We only want positive step values. */
1639 if (step < 0.0) {
1640 step *= -1;
1641 }
1642 }
1643
1644 /* Initialize the return_value as an array. */
1645 array_init(return_value);
1646
1647 /* If the range is given as strings, generate an array of characters. */
1648 if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
1649 int type1, type2;
1650 unsigned char *low, *high;
1651 long lstep = (long) step;
1652
1653 type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
1654 type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
1655
1656 if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
1657 goto double_str;
1658 } else if (type1 == IS_LONG || type2 == IS_LONG) {
1659 goto long_str;
1660 }
1661
1662 convert_to_string(zlow);
1663 convert_to_string(zhigh);
1664 low = (unsigned char *)Z_STRVAL_P(zlow);
1665 high = (unsigned char *)Z_STRVAL_P(zhigh);
1666
1667 if (*low > *high) { /* Negative Steps */
1668 if (lstep <= 0) {
1669 err = 1;
1670 goto err;
1671 }
1672 for (; *low >= *high; (*low) -= (unsigned int)lstep) {
1673 add_next_index_stringl(return_value, (const char *)low, 1, 1);
1674 if (((signed int)*low - lstep) < 0) {
1675 break;
1676 }
1677 }
1678 } else if (*high > *low) { /* Positive Steps */
1679 if (lstep <= 0) {
1680 err = 1;
1681 goto err;
1682 }
1683 for (; *low <= *high; (*low) += (unsigned int)lstep) {
1684 add_next_index_stringl(return_value, (const char *)low, 1, 1);
1685 if (((signed int)*low + lstep) > 255) {
1686 break;
1687 }
1688 }
1689 } else {
1690 add_next_index_stringl(return_value, (const char *)low, 1, 1);
1691 }
1692
1693 } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
1694 double low, high, value;
1695 long i;
1696 double_str:
1697 convert_to_double(zlow);
1698 convert_to_double(zhigh);
1699 low = Z_DVAL_P(zlow);
1700 high = Z_DVAL_P(zhigh);
1701 i = 0;
1702
1703 if (low > high) { /* Negative steps */
1704 if (low - high < step || step <= 0) {
1705 err = 1;
1706 goto err;
1707 }
1708
1709 for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) {
1710 add_next_index_double(return_value, value);
1711 }
1712 } else if (high > low) { /* Positive steps */
1713 if (high - low < step || step <= 0) {
1714 err = 1;
1715 goto err;
1716 }
1717
1718 for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) {
1719 add_next_index_double(return_value, value);
1720 }
1721 } else {
1722 add_next_index_double(return_value, low);
1723 }
1724 } else {
1725 double low, high;
1726 long lstep;
1727 long_str:
1728 convert_to_double(zlow);
1729 convert_to_double(zhigh);
1730 low = Z_DVAL_P(zlow);
1731 high = Z_DVAL_P(zhigh);
1732 lstep = (long) step;
1733
1734 if (low > high) { /* Negative steps */
1735 if (low - high < lstep || lstep <= 0) {
1736 err = 1;
1737 goto err;
1738 }
1739 for (; low >= high; low -= lstep) {
1740 add_next_index_long(return_value, (long)low);
1741 }
1742 } else if (high > low) { /* Positive steps */
1743 if (high - low < lstep || lstep <= 0) {
1744 err = 1;
1745 goto err;
1746 }
1747 for (; low <= high; low += lstep) {
1748 add_next_index_long(return_value, (long)low);
1749 }
1750 } else {
1751 add_next_index_long(return_value, (long)low);
1752 }
1753 }
1754 err:
1755 if (err) {
1756 php_error_docref(NULL TSRMLS_CC, E_WARNING, "step exceeds the specified range");
1757 zval_dtor(return_value);
1758 RETURN_FALSE;
1759 }
1760 }
1761 /* }}} */
1762
php_array_data_shuffle(zval * array TSRMLS_DC)1763 static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */
1764 {
1765 Bucket **elems, *temp;
1766 HashTable *hash;
1767 int j, n_elems, rnd_idx, n_left;
1768
1769 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
1770
1771 if (n_elems < 1) {
1772 return;
1773 }
1774
1775 elems = (Bucket **)safe_emalloc(n_elems, sizeof(Bucket *), 0);
1776 hash = Z_ARRVAL_P(array);
1777 n_left = n_elems;
1778
1779 for (j = 0, temp = hash->pListHead; temp; temp = temp->pListNext)
1780 elems[j++] = temp;
1781 while (--n_left) {
1782 rnd_idx = php_rand(TSRMLS_C);
1783 RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
1784 if (rnd_idx != n_left) {
1785 temp = elems[n_left];
1786 elems[n_left] = elems[rnd_idx];
1787 elems[rnd_idx] = temp;
1788 }
1789 }
1790
1791 HANDLE_BLOCK_INTERRUPTIONS();
1792 hash->pListHead = elems[0];
1793 hash->pListTail = NULL;
1794 hash->pInternalPointer = hash->pListHead;
1795
1796 for (j = 0; j < n_elems; j++) {
1797 if (hash->pListTail) {
1798 hash->pListTail->pListNext = elems[j];
1799 }
1800 elems[j]->pListLast = hash->pListTail;
1801 elems[j]->pListNext = NULL;
1802 hash->pListTail = elems[j];
1803 }
1804 temp = hash->pListHead;
1805 j = 0;
1806 while (temp != NULL) {
1807 temp->nKeyLength = 0;
1808 temp->h = j++;
1809 temp = temp->pListNext;
1810 }
1811 hash->nNextFreeElement = n_elems;
1812 zend_hash_rehash(hash);
1813 HANDLE_UNBLOCK_INTERRUPTIONS();
1814
1815 efree(elems);
1816 }
1817 /* }}} */
1818
1819 /* {{{ proto bool shuffle(array array_arg)
1820 Randomly shuffle the contents of an array */
PHP_FUNCTION(shuffle)1821 PHP_FUNCTION(shuffle)
1822 {
1823 zval *array;
1824
1825 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
1826 RETURN_FALSE;
1827 }
1828
1829 php_array_data_shuffle(array TSRMLS_CC);
1830
1831 RETURN_TRUE;
1832 }
1833 /* }}} */
1834
php_splice(HashTable * in_hash,int offset,int length,zval *** list,int list_count,HashTable ** removed)1835 PHPAPI HashTable* php_splice(HashTable *in_hash, int offset, int length, zval ***list, int list_count, HashTable **removed) /* {{{ */
1836 {
1837 HashTable *out_hash = NULL; /* Output hashtable */
1838 int num_in, /* Number of entries in the input hashtable */
1839 pos, /* Current position in the hashtable */
1840 i; /* Loop counter */
1841 Bucket *p; /* Pointer to hash bucket */
1842 zval *entry; /* Hash entry */
1843
1844 /* If input hash doesn't exist, we have nothing to do */
1845 if (!in_hash) {
1846 return NULL;
1847 }
1848
1849 /* Get number of entries in the input hash */
1850 num_in = zend_hash_num_elements(in_hash);
1851
1852 /* Clamp the offset.. */
1853 if (offset > num_in) {
1854 offset = num_in;
1855 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
1856 offset = 0;
1857 }
1858
1859 /* ..and the length */
1860 if (length < 0) {
1861 length = num_in - offset + length;
1862 } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
1863 length = num_in - offset;
1864 }
1865
1866 /* Create and initialize output hash */
1867 ALLOC_HASHTABLE(out_hash);
1868 zend_hash_init(out_hash, (length > 0 ? num_in - length : 0) + list_count, NULL, ZVAL_PTR_DTOR, 0);
1869
1870 /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
1871 for (pos = 0, p = in_hash->pListHead; pos < offset && p ; pos++, p = p->pListNext) {
1872 /* Get entry and increase reference count */
1873 entry = *((zval **)p->pData);
1874 Z_ADDREF_P(entry);
1875
1876 /* Update output hash depending on key type */
1877 if (p->nKeyLength == 0) {
1878 zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
1879 } else {
1880 zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
1881 }
1882 }
1883
1884 /* If hash for removed entries exists, go until offset+length and copy the entries to it */
1885 if (removed != NULL) {
1886 for ( ; pos < offset + length && p; pos++, p = p->pListNext) {
1887 entry = *((zval **)p->pData);
1888 Z_ADDREF_P(entry);
1889 if (p->nKeyLength == 0) {
1890 zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
1891 } else {
1892 zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
1893 }
1894 }
1895 } else { /* otherwise just skip those entries */
1896 for ( ; pos < offset + length && p; pos++, p = p->pListNext);
1897 }
1898
1899 /* If there are entries to insert.. */
1900 if (list != NULL) {
1901 /* ..for each one, create a new zval, copy entry into it and copy it into the output hash */
1902 for (i = 0; i < list_count; i++) {
1903 entry = *list[i];
1904 Z_ADDREF_P(entry);
1905 zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
1906 }
1907 }
1908
1909 /* Copy the remaining input hash entries to the output hash */
1910 for ( ; p ; p = p->pListNext) {
1911 entry = *((zval **)p->pData);
1912 Z_ADDREF_P(entry);
1913 if (p->nKeyLength == 0) {
1914 zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
1915 } else {
1916 zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
1917 }
1918 }
1919
1920 zend_hash_internal_pointer_reset(out_hash);
1921 return out_hash;
1922 }
1923 /* }}} */
1924
1925 /* {{{ proto int array_push(array stack, mixed var [, mixed ...])
1926 Pushes elements onto the end of the array */
PHP_FUNCTION(array_push)1927 PHP_FUNCTION(array_push)
1928 {
1929 zval ***args, /* Function arguments array */
1930 *stack, /* Input array */
1931 *new_var; /* Variable to be pushed */
1932 int i, /* Loop counter */
1933 argc; /* Number of function arguments */
1934
1935
1936 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
1937 return;
1938 }
1939
1940 /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
1941 for (i = 0; i < argc; i++) {
1942 new_var = *args[i];
1943 Z_ADDREF_P(new_var);
1944
1945 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var, sizeof(zval *), NULL) == FAILURE) {
1946 Z_DELREF_P(new_var);
1947 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
1948 efree(args);
1949 RETURN_FALSE;
1950 }
1951 }
1952
1953 /* Clean up and return the number of values in the stack */
1954 efree(args);
1955 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
1956 }
1957 /* }}} */
1958
1959 /* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */
_phpi_pop(INTERNAL_FUNCTION_PARAMETERS,int off_the_end)1960 static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
1961 {
1962 zval *stack, /* Input stack */
1963 **val; /* Value to be popped */
1964 char *key = NULL;
1965 uint key_len = 0;
1966 ulong index;
1967
1968 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &stack) == FAILURE) {
1969 return;
1970 }
1971
1972 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
1973 return;
1974 }
1975
1976 /* Get the first or last value and copy it into the return value */
1977 if (off_the_end) {
1978 zend_hash_internal_pointer_end(Z_ARRVAL_P(stack));
1979 } else {
1980 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
1981 }
1982 zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val);
1983 RETVAL_ZVAL(*val, 1, 0);
1984
1985 /* Delete the first or last value */
1986 zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL);
1987 if (key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
1988 zend_delete_global_variable(key, key_len - 1 TSRMLS_CC);
1989 } else {
1990 zend_hash_del_key_or_index(Z_ARRVAL_P(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX);
1991 }
1992
1993 /* If we did a shift... re-index like it did before */
1994 if (!off_the_end) {
1995 unsigned int k = 0;
1996 int should_rehash = 0;
1997 Bucket *p = Z_ARRVAL_P(stack)->pListHead;
1998 while (p != NULL) {
1999 if (p->nKeyLength == 0) {
2000 if (p->h != k) {
2001 p->h = k++;
2002 should_rehash = 1;
2003 } else {
2004 k++;
2005 }
2006 }
2007 p = p->pListNext;
2008 }
2009 Z_ARRVAL_P(stack)->nNextFreeElement = k;
2010 if (should_rehash) {
2011 zend_hash_rehash(Z_ARRVAL_P(stack));
2012 }
2013 } else if (!key_len && index >= Z_ARRVAL_P(stack)->nNextFreeElement - 1) {
2014 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
2015 }
2016
2017 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2018 }
2019 /* }}} */
2020
2021 /* {{{ proto mixed array_pop(array stack)
2022 Pops an element off the end of the array */
PHP_FUNCTION(array_pop)2023 PHP_FUNCTION(array_pop)
2024 {
2025 _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2026 }
2027 /* }}} */
2028
2029 /* {{{ proto mixed array_shift(array stack)
2030 Pops an element off the beginning of the array */
PHP_FUNCTION(array_shift)2031 PHP_FUNCTION(array_shift)
2032 {
2033 _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2034 }
2035 /* }}} */
2036
2037 /* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
2038 Pushes elements onto the beginning of the array */
PHP_FUNCTION(array_unshift)2039 PHP_FUNCTION(array_unshift)
2040 {
2041 zval ***args, /* Function arguments array */
2042 *stack; /* Input stack */
2043 HashTable *new_hash; /* New hashtable for the stack */
2044 HashTable old_hash;
2045 int argc; /* Number of function arguments */
2046
2047 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
2048 return;
2049 }
2050
2051 /* Use splice to insert the elements at the beginning. Destroy old
2052 * hashtable and replace it with new one */
2053 new_hash = php_splice(Z_ARRVAL_P(stack), 0, 0, &args[0], argc, NULL);
2054 old_hash = *Z_ARRVAL_P(stack);
2055 if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
2056 zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
2057 }
2058 *Z_ARRVAL_P(stack) = *new_hash;
2059 FREE_HASHTABLE(new_hash);
2060 zend_hash_destroy(&old_hash);
2061
2062 /* Clean up and return the number of elements in the stack */
2063 efree(args);
2064 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
2065 }
2066 /* }}} */
2067
2068 /* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
2069 Removes the elements designated by offset and length and replace them with supplied array */
PHP_FUNCTION(array_splice)2070 PHP_FUNCTION(array_splice)
2071 {
2072 zval *array, /* Input array */
2073 *repl_array = NULL, /* Replacement array */
2074 ***repl = NULL; /* Replacement elements */
2075 HashTable *new_hash = NULL, /* Output array's hash */
2076 **rem_hash = NULL; /* Removed elements' hash */
2077 HashTable old_hash;
2078 Bucket *p; /* Bucket used for traversing hash */
2079 long i,
2080 offset,
2081 length = 0,
2082 repl_num = 0; /* Number of replacement elements */
2083 int num_in; /* Number of elements in the input array */
2084
2085 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
2086 return;
2087 }
2088
2089 num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
2090
2091 if (ZEND_NUM_ARGS() < 3) {
2092 length = num_in;
2093 }
2094
2095 if (ZEND_NUM_ARGS() == 4) {
2096 /* Make sure the last argument, if passed, is an array */
2097 convert_to_array(repl_array);
2098
2099 /* Create the array of replacement elements */
2100 repl_num = zend_hash_num_elements(Z_ARRVAL_P(repl_array));
2101 repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0);
2102 for (p = Z_ARRVAL_P(repl_array)->pListHead, i = 0; p; p = p->pListNext, i++) {
2103 repl[i] = ((zval **)p->pData);
2104 }
2105 }
2106
2107 /* Don't create the array of removed elements if it's not going
2108 * to be used; e.g. only removing and/or replacing elements */
2109 if (return_value_used) {
2110 int size = length;
2111
2112 /* Clamp the offset.. */
2113 if (offset > num_in) {
2114 offset = num_in;
2115 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2116 offset = 0;
2117 }
2118
2119 /* ..and the length */
2120 if (length < 0) {
2121 size = num_in - offset + length;
2122 } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
2123 size = num_in - offset;
2124 }
2125
2126 /* Initialize return value */
2127 array_init_size(return_value, size > 0 ? size : 0);
2128 rem_hash = &Z_ARRVAL_P(return_value);
2129 }
2130
2131 /* Perform splice */
2132 new_hash = php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash);
2133
2134 /* Replace input array's hashtable with the new one */
2135 old_hash = *Z_ARRVAL_P(array);
2136 if (Z_ARRVAL_P(array) == &EG(symbol_table)) {
2137 zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
2138 }
2139 *Z_ARRVAL_P(array) = *new_hash;
2140 FREE_HASHTABLE(new_hash);
2141 zend_hash_destroy(&old_hash);
2142
2143 /* Clean up */
2144 if (ZEND_NUM_ARGS() == 4) {
2145 efree(repl);
2146 }
2147 }
2148 /* }}} */
2149
2150 /* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
2151 Returns elements specified by offset and length */
PHP_FUNCTION(array_slice)2152 PHP_FUNCTION(array_slice)
2153 {
2154 zval *input, /* Input array */
2155 **z_length = NULL, /* How many elements to get */
2156 **entry; /* An array entry */
2157 long offset, /* Offset to get elements from */
2158 length = 0;
2159 zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */
2160 int num_in, /* Number of elements in the input array */
2161 pos; /* Current position in the array */
2162 char *string_key;
2163 uint string_key_len;
2164 ulong num_key;
2165 HashPosition hpos;
2166
2167 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|Zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
2168 return;
2169 }
2170
2171 /* Get number of entries in the input hash */
2172 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
2173
2174 /* We want all entries from offset to the end if length is not passed or is null */
2175 if (ZEND_NUM_ARGS() < 3 || Z_TYPE_PP(z_length) == IS_NULL) {
2176 length = num_in;
2177 } else {
2178 convert_to_long_ex(z_length);
2179 length = Z_LVAL_PP(z_length);
2180 }
2181
2182 /* Clamp the offset.. */
2183 if (offset > num_in) {
2184 array_init(return_value);
2185 return;
2186 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2187 offset = 0;
2188 }
2189
2190 /* ..and the length */
2191 if (length < 0) {
2192 length = num_in - offset + length;
2193 } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
2194 length = num_in - offset;
2195 }
2196
2197 /* Initialize returned array */
2198 array_init_size(return_value, length > 0 ? length : 0);
2199
2200 if (length <= 0) {
2201 return;
2202 }
2203
2204 /* Start at the beginning and go until we hit offset */
2205 pos = 0;
2206 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &hpos);
2207 while (pos < offset && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
2208 pos++;
2209 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
2210 }
2211
2212 /* Copy elements from input array to the one that's returned */
2213 while (pos < offset + length && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
2214
2215 zval_add_ref(entry);
2216
2217 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &hpos)) {
2218 case HASH_KEY_IS_STRING:
2219 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
2220 break;
2221
2222 case HASH_KEY_IS_LONG:
2223 if (preserve_keys) {
2224 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
2225 } else {
2226 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2227 }
2228 break;
2229 }
2230 pos++;
2231 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
2232 }
2233 }
2234 /* }}} */
2235
php_array_merge(HashTable * dest,HashTable * src,int recursive TSRMLS_DC)2236 PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */
2237 {
2238 zval **src_entry, **dest_entry;
2239 char *string_key;
2240 uint string_key_len;
2241 ulong num_key;
2242 HashPosition pos;
2243
2244 zend_hash_internal_pointer_reset_ex(src, &pos);
2245 while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) {
2246 switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
2247 case HASH_KEY_IS_STRING:
2248 if (recursive && zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == SUCCESS) {
2249 HashTable *thash = Z_TYPE_PP(dest_entry) == IS_ARRAY ? Z_ARRVAL_PP(dest_entry) : NULL;
2250
2251 if ((thash && thash->nApplyCount > 1) || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) {
2252 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2253 return 0;
2254 }
2255 SEPARATE_ZVAL(dest_entry);
2256 SEPARATE_ZVAL(src_entry);
2257
2258 if (Z_TYPE_PP(dest_entry) == IS_NULL) {
2259 convert_to_array_ex(dest_entry);
2260 add_next_index_null(*dest_entry);
2261 } else {
2262 convert_to_array_ex(dest_entry);
2263 }
2264 if (Z_TYPE_PP(src_entry) == IS_NULL) {
2265 convert_to_array_ex(src_entry);
2266 add_next_index_null(*src_entry);
2267 } else {
2268 convert_to_array_ex(src_entry);
2269 }
2270 if (thash) {
2271 thash->nApplyCount++;
2272 }
2273 if (!php_array_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry), recursive TSRMLS_CC)) {
2274 if (thash) {
2275 thash->nApplyCount--;
2276 }
2277 return 0;
2278 }
2279 if (thash) {
2280 thash->nApplyCount--;
2281 }
2282 } else {
2283 Z_ADDREF_PP(src_entry);
2284 zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
2285 }
2286 break;
2287
2288 case HASH_KEY_IS_LONG:
2289 Z_ADDREF_PP(src_entry);
2290 zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL);
2291 break;
2292 }
2293 zend_hash_move_forward_ex(src, &pos);
2294 }
2295 return 1;
2296 }
2297 /* }}} */
2298
php_array_replace_recursive(HashTable * dest,HashTable * src TSRMLS_DC)2299 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */
2300 {
2301 zval **src_entry, **dest_entry;
2302 char *string_key;
2303 uint string_key_len;
2304 ulong num_key;
2305 HashPosition pos;
2306
2307 for (zend_hash_internal_pointer_reset_ex(src, &pos);
2308 zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS;
2309 zend_hash_move_forward_ex(src, &pos)) {
2310 switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
2311 case HASH_KEY_IS_STRING:
2312 if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
2313 zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == FAILURE ||
2314 Z_TYPE_PP(dest_entry) != IS_ARRAY) {
2315
2316 Z_ADDREF_PP(src_entry);
2317 zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
2318
2319 continue;
2320 }
2321 break;
2322
2323 case HASH_KEY_IS_LONG:
2324 if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
2325 zend_hash_index_find(dest, num_key, (void **)&dest_entry) == FAILURE ||
2326 Z_TYPE_PP(dest_entry) != IS_ARRAY) {
2327
2328 Z_ADDREF_PP(src_entry);
2329 zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL);
2330
2331 continue;
2332 }
2333 break;
2334 }
2335
2336 if (Z_ARRVAL_PP(dest_entry)->nApplyCount > 1 || Z_ARRVAL_PP(src_entry)->nApplyCount > 1 || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) {
2337 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2338 return 0;
2339 }
2340 SEPARATE_ZVAL(dest_entry);
2341 Z_ARRVAL_PP(dest_entry)->nApplyCount++;
2342 Z_ARRVAL_PP(src_entry)->nApplyCount++;
2343
2344
2345 if (!php_array_replace_recursive(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC)) {
2346 Z_ARRVAL_PP(dest_entry)->nApplyCount--;
2347 Z_ARRVAL_PP(src_entry)->nApplyCount--;
2348 return 0;
2349 }
2350 Z_ARRVAL_PP(dest_entry)->nApplyCount--;
2351 Z_ARRVAL_PP(src_entry)->nApplyCount--;
2352 }
2353
2354 return 1;
2355 }
2356 /* }}} */
2357
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS,int recursive,int replace)2358 static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
2359 {
2360 zval ***args = NULL;
2361 int argc, i, init_size = 0;
2362
2363 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
2364 return;
2365 }
2366
2367 for (i = 0; i < argc; i++) {
2368 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
2369 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
2370 efree(args);
2371 RETURN_NULL();
2372 } else {
2373 int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i]));
2374
2375 if (num > init_size) {
2376 init_size = num;
2377 }
2378 }
2379 }
2380
2381 array_init_size(return_value, init_size);
2382
2383 for (i = 0; i < argc; i++) {
2384 SEPARATE_ZVAL(args[i]);
2385 if (!replace) {
2386 php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
2387 } else if (recursive && i > 0) { /* First array will be copied directly instead */
2388 php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC);
2389 } else {
2390 zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1);
2391 }
2392 }
2393
2394 efree(args);
2395 }
2396 /* }}} */
2397
2398 /* {{{ proto array array_merge(array arr1, array arr2 [, array ...])
2399 Merges elements from passed arrays into one array */
PHP_FUNCTION(array_merge)2400 PHP_FUNCTION(array_merge)
2401 {
2402 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
2403 }
2404 /* }}} */
2405
2406 /* {{{ proto array array_merge_recursive(array arr1, array arr2 [, array ...])
2407 Recursively merges elements from passed arrays into one array */
PHP_FUNCTION(array_merge_recursive)2408 PHP_FUNCTION(array_merge_recursive)
2409 {
2410 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
2411 }
2412 /* }}} */
2413
2414 /* {{{ proto array array_replace(array arr1, array arr2 [, array ...])
2415 Replaces elements from passed arrays into one array */
PHP_FUNCTION(array_replace)2416 PHP_FUNCTION(array_replace)
2417 {
2418 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
2419 }
2420 /* }}} */
2421
2422 /* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...])
2423 Recursively replaces elements from passed arrays into one array */
PHP_FUNCTION(array_replace_recursive)2424 PHP_FUNCTION(array_replace_recursive)
2425 {
2426 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
2427 }
2428 /* }}} */
2429
2430 /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
2431 Return just the keys from the input array, optionally only for the specified search_value */
PHP_FUNCTION(array_keys)2432 PHP_FUNCTION(array_keys)
2433 {
2434 zval *input, /* Input array */
2435 *search_value = NULL, /* Value to search for */
2436 **entry, /* An entry in the input array */
2437 res, /* Result of comparison */
2438 *new_val; /* New value */
2439 int add_key; /* Flag to indicate whether a key should be added */
2440 char *string_key; /* String key */
2441 uint string_key_len;
2442 ulong num_key; /* Numeric key */
2443 zend_bool strict = 0; /* do strict comparison */
2444 HashPosition pos;
2445 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
2446
2447 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
2448 return;
2449 }
2450
2451 if (strict) {
2452 is_equal_func = is_identical_function;
2453 }
2454
2455 /* Initialize return array */
2456 if (search_value != NULL) {
2457 array_init(return_value);
2458 } else {
2459 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2460 }
2461 add_key = 1;
2462
2463 /* Go through input array and add keys to the return array */
2464 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
2465 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2466 if (search_value != NULL) {
2467 is_equal_func(&res, search_value, *entry TSRMLS_CC);
2468 add_key = zval_is_true(&res);
2469 }
2470
2471 if (add_key) {
2472 MAKE_STD_ZVAL(new_val);
2473
2474 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
2475 case HASH_KEY_IS_STRING:
2476 ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
2477 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
2478 break;
2479
2480 case HASH_KEY_IS_LONG:
2481 Z_TYPE_P(new_val) = IS_LONG;
2482 Z_LVAL_P(new_val) = num_key;
2483 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
2484 break;
2485 }
2486 }
2487
2488 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
2489 }
2490 }
2491 /* }}} */
2492
2493 /* {{{ proto array array_values(array input)
2494 Return just the values from the input array */
PHP_FUNCTION(array_values)2495 PHP_FUNCTION(array_values)
2496 {
2497 zval *input, /* Input array */
2498 **entry; /* An entry in the input array */
2499 HashPosition pos;
2500
2501 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2502 return;
2503 }
2504
2505 /* Initialize return array */
2506 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2507
2508 /* Go through input array and add values to the return array */
2509 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
2510 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2511 zval_add_ref(entry);
2512 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2513 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
2514 }
2515 }
2516 /* }}} */
2517
2518 /* {{{ proto array array_count_values(array input)
2519 Return the value as key and the frequency of that value in input as value */
PHP_FUNCTION(array_count_values)2520 PHP_FUNCTION(array_count_values)
2521 {
2522 zval *input, /* Input array */
2523 **entry, /* An entry in the input array */
2524 **tmp;
2525 HashTable *myht;
2526 HashPosition pos;
2527
2528 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2529 return;
2530 }
2531
2532 /* Initialize return array */
2533 array_init(return_value);
2534
2535 /* Go through input array and add values to the return array */
2536 myht = Z_ARRVAL_P(input);
2537 zend_hash_internal_pointer_reset_ex(myht, &pos);
2538 while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) {
2539 if (Z_TYPE_PP(entry) == IS_LONG) {
2540 if (zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), (void **)&tmp) == FAILURE) {
2541 zval *data;
2542 MAKE_STD_ZVAL(data);
2543 ZVAL_LONG(data, 1);
2544 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
2545 } else {
2546 Z_LVAL_PP(tmp)++;
2547 }
2548 } else if (Z_TYPE_PP(entry) == IS_STRING) {
2549 if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) {
2550 zval *data;
2551 MAKE_STD_ZVAL(data);
2552 ZVAL_LONG(data, 1);
2553 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
2554 } else {
2555 Z_LVAL_PP(tmp)++;
2556 }
2557 } else {
2558 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
2559 }
2560
2561 zend_hash_move_forward_ex(myht, &pos);
2562 }
2563 }
2564 /* }}} */
2565
2566 /* {{{ proto array array_reverse(array input [, bool preserve keys])
2567 Return input as a new array with the order of the entries reversed */
PHP_FUNCTION(array_reverse)2568 PHP_FUNCTION(array_reverse)
2569 {
2570 zval *input, /* Input array */
2571 **entry; /* An entry in the input array */
2572 char *string_key;
2573 uint string_key_len;
2574 ulong num_key;
2575 zend_bool preserve_keys = 0; /* whether to preserve keys */
2576 HashPosition pos;
2577
2578 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &input, &preserve_keys) == FAILURE) {
2579 return;
2580 }
2581
2582 /* Initialize return array */
2583 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2584
2585 zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos);
2586 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2587 zval_add_ref(entry);
2588
2589 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) {
2590 case HASH_KEY_IS_STRING:
2591 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
2592 break;
2593
2594 case HASH_KEY_IS_LONG:
2595 if (preserve_keys) {
2596 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
2597 } else {
2598 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2599 }
2600 break;
2601 }
2602
2603 zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos);
2604 }
2605 }
2606 /* }}} */
2607
2608 /* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
2609 Returns a copy of input array padded with pad_value to size pad_size */
PHP_FUNCTION(array_pad)2610 PHP_FUNCTION(array_pad)
2611 {
2612 zval *input; /* Input array */
2613 zval *pad_value; /* Padding value obviously */
2614 zval ***pads; /* Array to pass to splice */
2615 HashTable *new_hash;/* Return value from splice */
2616 HashTable old_hash;
2617 long pad_size; /* Size to pad to */
2618 long pad_size_abs; /* Absolute value of pad_size */
2619 int input_size; /* Size of the input array */
2620 int num_pads; /* How many pads do we need */
2621 int do_pad; /* Whether we should do padding at all */
2622 int i;
2623
2624 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "alz", &input, &pad_size, &pad_value) == FAILURE) {
2625 return;
2626 }
2627
2628 /* Do some initial calculations */
2629 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
2630 pad_size_abs = abs(pad_size);
2631 if (pad_size_abs < 0) {
2632 php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2633 zval_dtor(return_value);
2634 RETURN_FALSE;
2635 }
2636 do_pad = (input_size >= pad_size_abs) ? 0 : 1;
2637
2638 /* Copy the original array */
2639 RETVAL_ZVAL(input, 1, 0);
2640
2641 /* If no need to pad, no need to continue */
2642 if (!do_pad) {
2643 return;
2644 }
2645
2646 /* Populate the pads array */
2647 num_pads = pad_size_abs - input_size;
2648 if (num_pads > 1048576) {
2649 php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2650 zval_dtor(return_value);
2651 RETURN_FALSE;
2652 }
2653 pads = (zval ***)safe_emalloc(num_pads, sizeof(zval **), 0);
2654 for (i = 0; i < num_pads; i++) {
2655 pads[i] = &pad_value;
2656 }
2657
2658 /* Pad on the right or on the left */
2659 if (pad_size > 0) {
2660 new_hash = php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL);
2661 } else {
2662 new_hash = php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL);
2663 }
2664
2665 /* Copy the result hash into return value */
2666 old_hash = *Z_ARRVAL_P(return_value);
2667 if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
2668 zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
2669 }
2670 *Z_ARRVAL_P(return_value) = *new_hash;
2671 FREE_HASHTABLE(new_hash);
2672 zend_hash_destroy(&old_hash);
2673
2674 /* Clean up */
2675 efree(pads);
2676 }
2677 /* }}} */
2678
2679 /* {{{ proto array array_flip(array input)
2680 Return array with key <-> value flipped */
PHP_FUNCTION(array_flip)2681 PHP_FUNCTION(array_flip)
2682 {
2683 zval *array, **entry, *data;
2684 char *string_key;
2685 uint str_key_len;
2686 ulong num_key;
2687 HashPosition pos;
2688
2689 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2690 return;
2691 }
2692
2693 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2694
2695 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
2696 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
2697 MAKE_STD_ZVAL(data);
2698 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 1, &pos)) {
2699 case HASH_KEY_IS_STRING:
2700 ZVAL_STRINGL(data, string_key, str_key_len - 1, 0);
2701 break;
2702 case HASH_KEY_IS_LONG:
2703 Z_TYPE_P(data) = IS_LONG;
2704 Z_LVAL_P(data) = num_key;
2705 break;
2706 }
2707
2708 if (Z_TYPE_PP(entry) == IS_LONG) {
2709 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
2710 } else if (Z_TYPE_PP(entry) == IS_STRING) {
2711 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
2712 } else {
2713 zval_ptr_dtor(&data); /* will free also zval structure */
2714 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
2715 }
2716
2717 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
2718 }
2719 }
2720 /* }}} */
2721
2722 /* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
2723 Retuns an array with all string keys lowercased [or uppercased] */
PHP_FUNCTION(array_change_key_case)2724 PHP_FUNCTION(array_change_key_case)
2725 {
2726 zval *array, **entry;
2727 char *string_key;
2728 char *new_key;
2729 uint str_key_len;
2730 ulong num_key;
2731 long change_to_upper=0;
2732 HashPosition pos;
2733
2734 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &change_to_upper) == FAILURE) {
2735 return;
2736 }
2737
2738 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2739
2740 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
2741 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
2742 zval_add_ref(entry);
2743
2744 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
2745 case HASH_KEY_IS_LONG:
2746 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(entry), NULL);
2747 break;
2748 case HASH_KEY_IS_STRING:
2749 new_key = estrndup(string_key, str_key_len - 1);
2750 if (change_to_upper) {
2751 php_strtoupper(new_key, str_key_len - 1);
2752 } else {
2753 php_strtolower(new_key, str_key_len - 1);
2754 }
2755 zend_hash_update(Z_ARRVAL_P(return_value), new_key, str_key_len, entry, sizeof(entry), NULL);
2756 efree(new_key);
2757 break;
2758 }
2759
2760 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
2761 }
2762 }
2763 /* }}} */
2764
2765 /* {{{ proto array array_unique(array input [, int sort_flags])
2766 Removes duplicate values from array */
PHP_FUNCTION(array_unique)2767 PHP_FUNCTION(array_unique)
2768 {
2769 zval *array, *tmp;
2770 Bucket *p;
2771 struct bucketindex {
2772 Bucket *b;
2773 unsigned int i;
2774 };
2775 struct bucketindex *arTmp, *cmpdata, *lastkept;
2776 unsigned int i;
2777 long sort_type = PHP_SORT_STRING;
2778
2779 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
2780 return;
2781 }
2782
2783 php_set_compare_func(sort_type TSRMLS_CC);
2784
2785 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2786 zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*));
2787
2788 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
2789 return;
2790 }
2791
2792 /* create and sort array with pointers to the target_hash buckets */
2793 arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent);
2794 if (!arTmp) {
2795 zval_dtor(return_value);
2796 RETURN_FALSE;
2797 }
2798 for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) {
2799 arTmp[i].b = p;
2800 arTmp[i].i = i;
2801 }
2802 arTmp[i].b = NULL;
2803 zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC);
2804
2805 /* go through the sorted array and delete duplicates from the copy */
2806 lastkept = arTmp;
2807 for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) {
2808 if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
2809 lastkept = cmpdata;
2810 } else {
2811 if (lastkept->i > cmpdata->i) {
2812 p = lastkept->b;
2813 lastkept = cmpdata;
2814 } else {
2815 p = cmpdata->b;
2816 }
2817 if (p->nKeyLength == 0) {
2818 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
2819 } else {
2820 if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
2821 zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC);
2822 } else {
2823 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
2824 }
2825 }
2826 }
2827 }
2828 pefree(arTmp, Z_ARRVAL_P(array)->persistent);
2829 }
2830 /* }}} */
2831
zval_compare(zval ** a,zval ** b TSRMLS_DC)2832 static int zval_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
2833 {
2834 zval result;
2835 zval *first;
2836 zval *second;
2837
2838 first = *((zval **) a);
2839 second = *((zval **) b);
2840
2841 if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
2842 return 0;
2843 }
2844
2845 if (Z_TYPE(result) == IS_DOUBLE) {
2846 if (Z_DVAL(result) < 0) {
2847 return -1;
2848 } else if (Z_DVAL(result) > 0) {
2849 return 1;
2850 } else {
2851 return 0;
2852 }
2853 }
2854
2855 convert_to_long(&result);
2856
2857 if (Z_LVAL(result) < 0) {
2858 return -1;
2859 } else if (Z_LVAL(result) > 0) {
2860 return 1;
2861 }
2862
2863 return 0;
2864 }
2865 /* }}} */
2866
zval_user_compare(zval ** a,zval ** b TSRMLS_DC)2867 static int zval_user_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
2868 {
2869 zval **args[2];
2870 zval *retval_ptr;
2871
2872 args[0] = (zval **) a;
2873 args[1] = (zval **) b;
2874
2875 BG(user_compare_fci).param_count = 2;
2876 BG(user_compare_fci).params = args;
2877 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
2878 BG(user_compare_fci).no_separation = 0;
2879
2880 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
2881 long retval;
2882
2883 convert_to_long_ex(&retval_ptr);
2884 retval = Z_LVAL_P(retval_ptr);
2885 zval_ptr_dtor(&retval_ptr);
2886 return retval < 0 ? -1 : retval > 0 ? 1 : 0;;
2887 } else {
2888 return 0;
2889 }
2890 }
2891 /* }}} */
2892
php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS,int data_compare_type)2893 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
2894 {
2895 Bucket *p;
2896 int argc, i;
2897 zval ***args;
2898 int (*intersect_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
2899 zend_bool ok;
2900 zval **data;
2901 int req_args;
2902 char *param_spec;
2903
2904 /* Get the argument count */
2905 argc = ZEND_NUM_ARGS();
2906 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
2907 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
2908 req_args = 3;
2909 param_spec = "+f";
2910 intersect_data_compare_func = zval_user_compare;
2911 } else {
2912 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
2913 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
2914 req_args = 2;
2915 param_spec = "+";
2916
2917 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
2918 intersect_data_compare_func = zval_compare;
2919 }
2920 }
2921
2922 if (argc < req_args) {
2923 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
2924 return;
2925 }
2926
2927 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
2928 return;
2929 }
2930
2931 for (i = 0; i < argc; i++) {
2932 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
2933 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
2934 RETVAL_NULL();
2935 goto out;
2936 }
2937 }
2938
2939 array_init(return_value);
2940
2941 for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
2942 if (p->nKeyLength == 0) {
2943 ok = 1;
2944 for (i = 1; i < argc; i++) {
2945 if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == FAILURE ||
2946 (intersect_data_compare_func &&
2947 intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
2948 ) {
2949 ok = 0;
2950 break;
2951 }
2952 }
2953 if (ok) {
2954 Z_ADDREF_PP((zval**)p->pData);
2955 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
2956 }
2957 } else {
2958 ok = 1;
2959 for (i = 1; i < argc; i++) {
2960 if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == FAILURE ||
2961 (intersect_data_compare_func &&
2962 intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
2963 ) {
2964 ok = 0;
2965 break;
2966 }
2967 }
2968 if (ok) {
2969 Z_ADDREF_PP((zval**)p->pData);
2970 zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
2971 }
2972 }
2973 }
2974 out:
2975 efree(args);
2976 }
2977 /* }}} */
2978
php_array_intersect(INTERNAL_FUNCTION_PARAMETERS,int behavior,int data_compare_type,int key_compare_type)2979 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
2980 {
2981 zval ***args = NULL;
2982 HashTable *hash;
2983 int arr_argc, i, c = 0;
2984 Bucket ***lists, **list, ***ptrs, *p;
2985 int req_args;
2986 char *param_spec;
2987 zend_fcall_info fci1, fci2;
2988 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
2989 zend_fcall_info *fci_key, *fci_data;
2990 zend_fcall_info_cache *fci_key_cache, *fci_data_cache;
2991 PHP_ARRAY_CMP_FUNC_VARS;
2992
2993 int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC);
2994 int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC);
2995
2996 if (behavior == INTERSECT_NORMAL) {
2997 intersect_key_compare_func = php_array_key_compare;
2998
2999 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
3000 /* array_intersect() */
3001 req_args = 2;
3002 param_spec = "+";
3003 intersect_data_compare_func = php_array_data_compare;
3004 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3005 /* array_uintersect() */
3006 req_args = 3;
3007 param_spec = "+f";
3008 intersect_data_compare_func = php_array_user_compare;
3009 } else {
3010 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
3011 return;
3012 }
3013
3014 if (ZEND_NUM_ARGS() < req_args) {
3015 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3016 return;
3017 }
3018
3019 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3020 return;
3021 }
3022 fci_data = &fci1;
3023 fci_data_cache = &fci1_cache;
3024
3025 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3026 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
3027 * no comparison of the data is done (part of INTERSECT_ASSOC) */
3028 intersect_key_compare_func = php_array_key_compare;
3029
3030 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
3031 /* array_intersect_assoc() or array_intersect_key() */
3032 req_args = 2;
3033 param_spec = "+";
3034 intersect_key_compare_func = php_array_key_compare;
3035 intersect_data_compare_func = php_array_data_compare;
3036 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
3037 /* array_uintersect_assoc() */
3038 req_args = 3;
3039 param_spec = "+f";
3040 intersect_key_compare_func = php_array_key_compare;
3041 intersect_data_compare_func = php_array_user_compare;
3042 fci_data = &fci1;
3043 fci_data_cache = &fci1_cache;
3044 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
3045 /* array_intersect_uassoc() or array_intersect_ukey() */
3046 req_args = 3;
3047 param_spec = "+f";
3048 intersect_key_compare_func = php_array_user_key_compare;
3049 intersect_data_compare_func = php_array_data_compare;
3050 fci_key = &fci1;
3051 fci_key_cache = &fci1_cache;
3052 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
3053 /* array_uintersect_uassoc() */
3054 req_args = 4;
3055 param_spec = "+ff";
3056 intersect_key_compare_func = php_array_user_key_compare;
3057 intersect_data_compare_func = php_array_user_compare;
3058 fci_data = &fci1;
3059 fci_data_cache = &fci1_cache;
3060 fci_key = &fci2;
3061 fci_key_cache = &fci2_cache;
3062 } else {
3063 php_error_docref(NULL TSRMLS_CC, 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);
3064 return;
3065 }
3066
3067 if (ZEND_NUM_ARGS() < req_args) {
3068 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3069 return;
3070 }
3071
3072 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
3073 return;
3074 }
3075
3076 } else {
3077 php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3078 return;
3079 }
3080
3081 PHP_ARRAY_CMP_FUNC_BACKUP();
3082
3083 /* for each argument, create and sort list with pointers to the hash buckets */
3084 lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3085 ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3086 php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3087
3088 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
3089 BG(user_compare_fci) = *fci_data;
3090 BG(user_compare_fci_cache) = *fci_data_cache;
3091 } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
3092 BG(user_compare_fci) = *fci_key;
3093 BG(user_compare_fci_cache) = *fci_key_cache;
3094 }
3095
3096 for (i = 0; i < arr_argc; i++) {
3097 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3098 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3099 arr_argc = i; /* only free up to i - 1 */
3100 goto out;
3101 }
3102 hash = Z_ARRVAL_PP(args[i]);
3103 list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
3104 if (!list) {
3105 PHP_ARRAY_CMP_FUNC_RESTORE();
3106
3107 efree(ptrs);
3108 efree(lists);
3109 efree(args);
3110 RETURN_FALSE;
3111 }
3112 lists[i] = list;
3113 ptrs[i] = list;
3114 for (p = hash->pListHead; p; p = p->pListNext) {
3115 *list++ = p;
3116 }
3117 *list = NULL;
3118 if (behavior == INTERSECT_NORMAL) {
3119 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC);
3120 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3121 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC);
3122 }
3123 }
3124
3125 /* copy the argument array */
3126 RETVAL_ZVAL(*args[0], 1, 0);
3127 if (return_value->value.ht == &EG(symbol_table)) {
3128 HashTable *ht;
3129 zval *tmp;
3130
3131 ALLOC_HASHTABLE(ht);
3132 zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
3133 zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
3134 return_value->value.ht = ht;
3135 }
3136
3137 /* go through the lists and look for common values */
3138 while (*ptrs[0]) {
3139 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
3140 &&
3141 key_compare_type == INTERSECT_COMP_KEY_USER) {
3142
3143 BG(user_compare_fci) = *fci_key;
3144 BG(user_compare_fci_cache) = *fci_key_cache;
3145 }
3146
3147 for (i = 1; i < arr_argc; i++) {
3148 if (behavior & INTERSECT_NORMAL) {
3149 while (*ptrs[i] && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3150 ptrs[i]++;
3151 }
3152 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3153 while (*ptrs[i] && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3154 ptrs[i]++;
3155 }
3156 if ((!c && *ptrs[i]) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
3157 /* this means that ptrs[i] is not NULL so we can compare
3158 * and "c==0" is from last operation
3159 * in this branch of code we enter only when INTERSECT_ASSOC
3160 * since when we have INTERSECT_KEY compare of data is not wanted. */
3161 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3162 BG(user_compare_fci) = *fci_data;
3163 BG(user_compare_fci_cache) = *fci_data_cache;
3164 }
3165 if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) {
3166 c = 1;
3167 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
3168 BG(user_compare_fci) = *fci_key;
3169 BG(user_compare_fci_cache) = *fci_key_cache;
3170 /* When KEY_USER, the last parameter is always the callback */
3171 }
3172 /* we are going to the break */
3173 } else {
3174 /* continue looping */
3175 }
3176 }
3177 }
3178 if (!*ptrs[i]) {
3179 /* delete any values corresponding to remains of ptrs[0] */
3180 /* and exit because they do not present in at least one of */
3181 /* the other arguments */
3182 for (;;) {
3183 p = *ptrs[0]++;
3184 if (!p) {
3185 goto out;
3186 }
3187 if (p->nKeyLength == 0) {
3188 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3189 } else {
3190 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3191 }
3192 }
3193 }
3194 if (c) /* here we get if not all are equal */
3195 break;
3196 ptrs[i]++;
3197 }
3198 if (c) {
3199 /* Value of ptrs[0] not in all arguments, delete all entries */
3200 /* with value < value of ptrs[i] */
3201 for (;;) {
3202 p = *ptrs[0];
3203 if (p->nKeyLength == 0) {
3204 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3205 } else {
3206 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3207 }
3208 if (!*++ptrs[0]) {
3209 goto out;
3210 }
3211 if (behavior == INTERSECT_NORMAL) {
3212 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) {
3213 break;
3214 }
3215 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3216 /* no need of looping because indexes are unique */
3217 break;
3218 }
3219 }
3220 } else {
3221 /* ptrs[0] is present in all the arguments */
3222 /* Skip all entries with same value as ptrs[0] */
3223 for (;;) {
3224 if (!*++ptrs[0]) {
3225 goto out;
3226 }
3227 if (behavior == INTERSECT_NORMAL) {
3228 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3229 break;
3230 }
3231 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3232 /* no need of looping because indexes are unique */
3233 break;
3234 }
3235 }
3236 }
3237 }
3238 out:
3239 for (i = 0; i < arr_argc; i++) {
3240 hash = Z_ARRVAL_PP(args[i]);
3241 pefree(lists[i], hash->persistent);
3242 }
3243
3244 PHP_ARRAY_CMP_FUNC_RESTORE();
3245
3246 efree(ptrs);
3247 efree(lists);
3248 efree(args);
3249 }
3250 /* }}} */
3251
3252 /* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
3253 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. */
PHP_FUNCTION(array_intersect_key)3254 PHP_FUNCTION(array_intersect_key)
3255 {
3256 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
3257 }
3258 /* }}} */
3259
3260 /* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
3261 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. */
PHP_FUNCTION(array_intersect_ukey)3262 PHP_FUNCTION(array_intersect_ukey)
3263 {
3264 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3265 }
3266 /* }}} */
3267
3268 /* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
3269 Returns the entries of arr1 that have values which are present in all the other arguments */
PHP_FUNCTION(array_intersect)3270 PHP_FUNCTION(array_intersect)
3271 {
3272 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
3273 }
3274 /* }}} */
3275
3276 /* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
3277 Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback. */
PHP_FUNCTION(array_uintersect)3278 PHP_FUNCTION(array_uintersect)
3279 {
3280 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
3281 }
3282 /* }}} */
3283
3284 /* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
3285 Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
PHP_FUNCTION(array_intersect_assoc)3286 PHP_FUNCTION(array_intersect_assoc)
3287 {
3288 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
3289 }
3290 /* }}} */
3291
3292 /* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
3293 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 an user-supplied callback. */
PHP_FUNCTION(array_intersect_uassoc)3294 PHP_FUNCTION(array_intersect_uassoc)
3295 {
3296 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3297 }
3298 /* }}} */
3299
3300 /* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
3301 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 an user-supplied callback. */
PHP_FUNCTION(array_uintersect_assoc)3302 PHP_FUNCTION(array_uintersect_assoc)
3303 {
3304 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
3305 }
3306 /* }}} */
3307
3308 /* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
3309 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. */
PHP_FUNCTION(array_uintersect_uassoc)3310 PHP_FUNCTION(array_uintersect_uassoc)
3311 {
3312 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
3313 }
3314 /* }}} */
3315
php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS,int data_compare_type)3316 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
3317 {
3318 Bucket *p;
3319 int argc, i;
3320 zval ***args;
3321 int (*diff_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
3322 zend_bool ok;
3323 zval **data;
3324
3325 /* Get the argument count */
3326 argc = ZEND_NUM_ARGS();
3327 if (data_compare_type == DIFF_COMP_DATA_USER) {
3328 if (argc < 3) {
3329 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
3330 return;
3331 }
3332 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
3333 return;
3334 }
3335 diff_data_compare_func = zval_user_compare;
3336 } else {
3337 if (argc < 2) {
3338 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
3339 return;
3340 }
3341 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3342 return;
3343 }
3344 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3345 diff_data_compare_func = zval_compare;
3346 }
3347 }
3348
3349 for (i = 0; i < argc; i++) {
3350 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3351 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3352 RETVAL_NULL();
3353 goto out;
3354 }
3355 }
3356
3357 array_init(return_value);
3358
3359 for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
3360 if (p->nKeyLength == 0) {
3361 ok = 1;
3362 for (i = 1; i < argc; i++) {
3363 if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS &&
3364 (!diff_data_compare_func ||
3365 diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
3366 ) {
3367 ok = 0;
3368 break;
3369 }
3370 }
3371 if (ok) {
3372 Z_ADDREF_PP((zval**)p->pData);
3373 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
3374 }
3375 } else {
3376 ok = 1;
3377 for (i = 1; i < argc; i++) {
3378 if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS &&
3379 (!diff_data_compare_func ||
3380 diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
3381 ) {
3382 ok = 0;
3383 break;
3384 }
3385 }
3386 if (ok) {
3387 Z_ADDREF_PP((zval**)p->pData);
3388 zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
3389 }
3390 }
3391 }
3392 out:
3393 efree(args);
3394 }
3395 /* }}} */
3396
php_array_diff(INTERNAL_FUNCTION_PARAMETERS,int behavior,int data_compare_type,int key_compare_type)3397 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
3398 {
3399 zval ***args = NULL;
3400 HashTable *hash;
3401 int arr_argc, i, c;
3402 Bucket ***lists, **list, ***ptrs, *p;
3403 int req_args;
3404 char *param_spec;
3405 zend_fcall_info fci1, fci2;
3406 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
3407 zend_fcall_info *fci_key, *fci_data;
3408 zend_fcall_info_cache *fci_key_cache, *fci_data_cache;
3409 PHP_ARRAY_CMP_FUNC_VARS;
3410
3411 int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC);
3412 int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC);
3413
3414 if (behavior == DIFF_NORMAL) {
3415 diff_key_compare_func = php_array_key_compare;
3416
3417 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3418 /* array_diff */
3419 req_args = 2;
3420 param_spec = "+";
3421 diff_data_compare_func = php_array_data_compare;
3422 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
3423 /* array_udiff */
3424 req_args = 3;
3425 param_spec = "+f";
3426 diff_data_compare_func = php_array_user_compare;
3427 } else {
3428 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
3429 return;
3430 }
3431
3432 if (ZEND_NUM_ARGS() < req_args) {
3433 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3434 return;
3435 }
3436
3437 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3438 return;
3439 }
3440 fci_data = &fci1;
3441 fci_data_cache = &fci1_cache;
3442
3443 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
3444 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
3445 * no comparison of the data is done (part of DIFF_ASSOC) */
3446
3447 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3448 /* array_diff_assoc() or array_diff_key() */
3449 req_args = 2;
3450 param_spec = "+";
3451 diff_key_compare_func = php_array_key_compare;
3452 diff_data_compare_func = php_array_data_compare;
3453 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3454 /* array_udiff_assoc() */
3455 req_args = 3;
3456 param_spec = "+f";
3457 diff_key_compare_func = php_array_key_compare;
3458 diff_data_compare_func = php_array_user_compare;
3459 fci_data = &fci1;
3460 fci_data_cache = &fci1_cache;
3461 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
3462 /* array_diff_uassoc() or array_diff_ukey() */
3463 req_args = 3;
3464 param_spec = "+f";
3465 diff_key_compare_func = php_array_user_key_compare;
3466 diff_data_compare_func = php_array_data_compare;
3467 fci_key = &fci1;
3468 fci_key_cache = &fci1_cache;
3469 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
3470 /* array_udiff_uassoc() */
3471 req_args = 4;
3472 param_spec = "+ff";
3473 diff_key_compare_func = php_array_user_key_compare;
3474 diff_data_compare_func = php_array_user_compare;
3475 fci_data = &fci1;
3476 fci_data_cache = &fci1_cache;
3477 fci_key = &fci2;
3478 fci_key_cache = &fci2_cache;
3479 } else {
3480 php_error_docref(NULL TSRMLS_CC, 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);
3481 return;
3482 }
3483
3484 if (ZEND_NUM_ARGS() < req_args) {
3485 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3486 return;
3487 }
3488
3489 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
3490 return;
3491 }
3492
3493 } else {
3494 php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3495 return;
3496 }
3497
3498 PHP_ARRAY_CMP_FUNC_BACKUP();
3499
3500 /* for each argument, create and sort list with pointers to the hash buckets */
3501 lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3502 ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3503 php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3504
3505 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
3506 BG(user_compare_fci) = *fci_data;
3507 BG(user_compare_fci_cache) = *fci_data_cache;
3508 } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
3509 BG(user_compare_fci) = *fci_key;
3510 BG(user_compare_fci_cache) = *fci_key_cache;
3511 }
3512
3513 for (i = 0; i < arr_argc; i++) {
3514 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3515 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3516 arr_argc = i; /* only free up to i - 1 */
3517 goto out;
3518 }
3519 hash = Z_ARRVAL_PP(args[i]);
3520 list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
3521 if (!list) {
3522 PHP_ARRAY_CMP_FUNC_RESTORE();
3523
3524 efree(ptrs);
3525 efree(lists);
3526 efree(args);
3527 RETURN_FALSE;
3528 }
3529 lists[i] = list;
3530 ptrs[i] = list;
3531 for (p = hash->pListHead; p; p = p->pListNext) {
3532 *list++ = p;
3533 }
3534 *list = NULL;
3535 if (behavior == DIFF_NORMAL) {
3536 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_data_compare_func TSRMLS_CC);
3537 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3538 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_key_compare_func TSRMLS_CC);
3539 }
3540 }
3541
3542 /* copy the argument array */
3543 RETVAL_ZVAL(*args[0], 1, 0);
3544 if (return_value->value.ht == &EG(symbol_table)) {
3545 HashTable *ht;
3546 zval *tmp;
3547
3548 ALLOC_HASHTABLE(ht);
3549 zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
3550 zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
3551 return_value->value.ht = ht;
3552 }
3553
3554 /* go through the lists and look for values of ptr[0] that are not in the others */
3555 while (*ptrs[0]) {
3556 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
3557 &&
3558 key_compare_type == DIFF_COMP_KEY_USER
3559 ) {
3560 BG(user_compare_fci) = *fci_key;
3561 BG(user_compare_fci_cache) = *fci_key_cache;
3562 }
3563 c = 1;
3564 for (i = 1; i < arr_argc; i++) {
3565 Bucket **ptr = ptrs[i];
3566 if (behavior == DIFF_NORMAL) {
3567 while (*ptrs[i] && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3568 ptrs[i]++;
3569 }
3570 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3571 while (*ptr && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
3572 ptr++;
3573 }
3574 }
3575 if (!c) {
3576 if (behavior == DIFF_NORMAL) {
3577 if (*ptrs[i]) {
3578 ptrs[i]++;
3579 }
3580 break;
3581 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
3582 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
3583 * data comparison is not needed - skipped. */
3584 if (*ptr) {
3585 if (data_compare_type == DIFF_COMP_DATA_USER) {
3586 BG(user_compare_fci) = *fci_data;
3587 BG(user_compare_fci_cache) = *fci_data_cache;
3588 }
3589 if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) {
3590 /* the data is not the same */
3591 c = -1;
3592 if (key_compare_type == DIFF_COMP_KEY_USER) {
3593 BG(user_compare_fci) = *fci_key;
3594 BG(user_compare_fci_cache) = *fci_key_cache;
3595 }
3596 } else {
3597 break;
3598 /* we have found the element in other arrays thus we don't want it
3599 * in the return_value -> delete from there */
3600 }
3601 }
3602 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
3603 /* the behavior here differs from INTERSECT_KEY in php_intersect
3604 * since in the "diff" case we have to remove the entry from
3605 * return_value while when doing intersection the entry must not
3606 * be deleted. */
3607 break; /* remove the key */
3608 }
3609 }
3610 }
3611 if (!c) {
3612 /* ptrs[0] in one of the other arguments */
3613 /* delete all entries with value as ptrs[0] */
3614 for (;;) {
3615 p = *ptrs[0];
3616 if (p->nKeyLength == 0) {
3617 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3618 } else {
3619 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3620 }
3621 if (!*++ptrs[0]) {
3622 goto out;
3623 }
3624 if (behavior == DIFF_NORMAL) {
3625 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3626 break;
3627 }
3628 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3629 /* in this case no array_key_compare is needed */
3630 break;
3631 }
3632 }
3633 } else {
3634 /* ptrs[0] in none of the other arguments */
3635 /* skip all entries with value as ptrs[0] */
3636 for (;;) {
3637 if (!*++ptrs[0]) {
3638 goto out;
3639 }
3640 if (behavior == DIFF_NORMAL) {
3641 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3642 break;
3643 }
3644 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3645 /* in this case no array_key_compare is needed */
3646 break;
3647 }
3648 }
3649 }
3650 }
3651 out:
3652 for (i = 0; i < arr_argc; i++) {
3653 hash = Z_ARRVAL_PP(args[i]);
3654 pefree(lists[i], hash->persistent);
3655 }
3656
3657 PHP_ARRAY_CMP_FUNC_RESTORE();
3658
3659 efree(ptrs);
3660 efree(lists);
3661 efree(args);
3662 }
3663 /* }}} */
3664
3665 /* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
3666 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. */
PHP_FUNCTION(array_diff_key)3667 PHP_FUNCTION(array_diff_key)
3668 {
3669 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
3670 }
3671 /* }}} */
3672
3673 /* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
3674 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. */
PHP_FUNCTION(array_diff_ukey)3675 PHP_FUNCTION(array_diff_ukey)
3676 {
3677 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3678 }
3679 /* }}} */
3680
3681 /* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
3682 Returns the entries of arr1 that have values which are not present in any of the others arguments. */
PHP_FUNCTION(array_diff)3683 PHP_FUNCTION(array_diff)
3684 {
3685 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
3686 }
3687 /* }}} */
3688
3689 /* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
3690 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. */
PHP_FUNCTION(array_udiff)3691 PHP_FUNCTION(array_udiff)
3692 {
3693 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
3694 }
3695 /* }}} */
3696
3697 /* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
3698 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 */
PHP_FUNCTION(array_diff_assoc)3699 PHP_FUNCTION(array_diff_assoc)
3700 {
3701 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
3702 }
3703 /* }}} */
3704
3705 /* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
3706 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. */
PHP_FUNCTION(array_diff_uassoc)3707 PHP_FUNCTION(array_diff_uassoc)
3708 {
3709 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3710 }
3711 /* }}} */
3712
3713 /* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
3714 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. */
PHP_FUNCTION(array_udiff_assoc)3715 PHP_FUNCTION(array_udiff_assoc)
3716 {
3717 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
3718 }
3719 /* }}} */
3720
3721 /* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
3722 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. */
PHP_FUNCTION(array_udiff_uassoc)3723 PHP_FUNCTION(array_udiff_uassoc)
3724 {
3725 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
3726 }
3727 /* }}} */
3728
3729 #define MULTISORT_ORDER 0
3730 #define MULTISORT_TYPE 1
3731 #define MULTISORT_LAST 2
3732
php_multisort_compare(const void * a,const void * b TSRMLS_DC)3733 PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
3734 {
3735 Bucket **ab = *(Bucket ***)a;
3736 Bucket **bb = *(Bucket ***)b;
3737 int r;
3738 int result = 0;
3739 zval temp;
3740
3741 r = 0;
3742 do {
3743 php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC);
3744
3745 ARRAYG(compare_func)(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData) TSRMLS_CC);
3746 result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp);
3747 if (result != 0) {
3748 return result;
3749 }
3750 r++;
3751 } while (ab[r] != NULL);
3752
3753 return result;
3754 }
3755 /* }}} */
3756
3757 #define MULTISORT_ABORT \
3758 for (k = 0; k < MULTISORT_LAST; k++) \
3759 efree(ARRAYG(multisort_flags)[k]); \
3760 efree(arrays); \
3761 efree(args); \
3762 RETURN_FALSE;
3763
3764 /* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]], ...])
3765 Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
PHP_FUNCTION(array_multisort)3766 PHP_FUNCTION(array_multisort)
3767 {
3768 zval*** args;
3769 zval*** arrays;
3770 Bucket*** indirect;
3771 Bucket* p;
3772 HashTable* hash;
3773 int argc;
3774 int array_size;
3775 int num_arrays = 0;
3776 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
3777 int sort_order = PHP_SORT_ASC;
3778 int sort_type = PHP_SORT_REGULAR;
3779 int i, k;
3780
3781 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3782 return;
3783 }
3784
3785 /* Allocate space for storing pointers to input arrays and sort flags. */
3786 arrays = (zval ***)ecalloc(argc, sizeof(zval **));
3787 for (i = 0; i < MULTISORT_LAST; i++) {
3788 parse_state[i] = 0;
3789 ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
3790 }
3791
3792 /* Here we go through the input arguments and parse them. Each one can
3793 * be either an array or a sort flag which follows an array. If not
3794 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
3795 * accordingly. There can't be two sort flags of the same type after an
3796 * array, and the very first argument has to be an array. */
3797 for (i = 0; i < argc; i++) {
3798 if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
3799 /* We see the next array, so we update the sort flags of
3800 * the previous array and reset the sort flags. */
3801 if (i > 0) {
3802 ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
3803 ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
3804 sort_order = PHP_SORT_ASC;
3805 sort_type = PHP_SORT_REGULAR;
3806 }
3807 arrays[num_arrays++] = args[i];
3808
3809 /* Next one may be an array or a list of sort flags. */
3810 for (k = 0; k < MULTISORT_LAST; k++) {
3811 parse_state[k] = 1;
3812 }
3813 } else if (Z_TYPE_PP(args[i]) == IS_LONG) {
3814 switch (Z_LVAL_PP(args[i])) {
3815 case PHP_SORT_ASC:
3816 case PHP_SORT_DESC:
3817 /* flag allowed here */
3818 if (parse_state[MULTISORT_ORDER] == 1) {
3819 /* Save the flag and make sure then next arg is not the current flag. */
3820 sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1;
3821 parse_state[MULTISORT_ORDER] = 0;
3822 } else {
3823 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
3824 MULTISORT_ABORT;
3825 }
3826 break;
3827
3828 case PHP_SORT_REGULAR:
3829 case PHP_SORT_NUMERIC:
3830 case PHP_SORT_STRING:
3831 #if HAVE_STRCOLL
3832 case PHP_SORT_LOCALE_STRING:
3833 #endif
3834 /* flag allowed here */
3835 if (parse_state[MULTISORT_TYPE] == 1) {
3836 /* Save the flag and make sure then next arg is not the current flag. */
3837 sort_type = Z_LVAL_PP(args[i]);
3838 parse_state[MULTISORT_TYPE] = 0;
3839 } else {
3840 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
3841 MULTISORT_ABORT;
3842 }
3843 break;
3844
3845 default:
3846 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
3847 MULTISORT_ABORT;
3848 break;
3849
3850 }
3851 } else {
3852 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
3853 MULTISORT_ABORT;
3854 }
3855 }
3856 /* Take care of the last array sort flags. */
3857 ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
3858 ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
3859
3860 /* Make sure the arrays are of the same size. */
3861 array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0]));
3862 for (i = 0; i < num_arrays; i++) {
3863 if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) {
3864 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
3865 MULTISORT_ABORT;
3866 }
3867 }
3868
3869 /* If all arrays are empty we don't need to do anything. */
3870 if (array_size < 1) {
3871 for (k = 0; k < MULTISORT_LAST; k++) {
3872 efree(ARRAYG(multisort_flags)[k]);
3873 }
3874 efree(arrays);
3875 efree(args);
3876 RETURN_TRUE;
3877 }
3878
3879 /* Create the indirection array. This array is of size MxN, where
3880 * M is the number of entries in each input array and N is the number
3881 * of the input arrays + 1. The last column is NULL to indicate the end
3882 * of the row. */
3883 indirect = (Bucket ***)safe_emalloc(array_size, sizeof(Bucket **), 0);
3884 for (i = 0; i < array_size; i++) {
3885 indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0);
3886 }
3887 for (i = 0; i < num_arrays; i++) {
3888 k = 0;
3889 for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) {
3890 indirect[k][i] = p;
3891 }
3892 }
3893 for (k = 0; k < array_size; k++) {
3894 indirect[k][num_arrays] = NULL;
3895 }
3896
3897 /* Do the actual sort magic - bada-bim, bada-boom. */
3898 zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
3899
3900 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
3901 HANDLE_BLOCK_INTERRUPTIONS();
3902 for (i = 0; i < num_arrays; i++) {
3903 hash = Z_ARRVAL_PP(arrays[i]);
3904 hash->pListHead = indirect[0][i];;
3905 hash->pListTail = NULL;
3906 hash->pInternalPointer = hash->pListHead;
3907
3908 for (k = 0; k < array_size; k++) {
3909 if (hash->pListTail) {
3910 hash->pListTail->pListNext = indirect[k][i];
3911 }
3912 indirect[k][i]->pListLast = hash->pListTail;
3913 indirect[k][i]->pListNext = NULL;
3914 hash->pListTail = indirect[k][i];
3915 }
3916
3917 p = hash->pListHead;
3918 k = 0;
3919 while (p != NULL) {
3920 if (p->nKeyLength == 0)
3921 p->h = k++;
3922 p = p->pListNext;
3923 }
3924 hash->nNextFreeElement = array_size;
3925 zend_hash_rehash(hash);
3926 }
3927 HANDLE_UNBLOCK_INTERRUPTIONS();
3928
3929 /* Clean up. */
3930 for (i = 0; i < array_size; i++) {
3931 efree(indirect[i]);
3932 }
3933 efree(indirect);
3934 for (k = 0; k < MULTISORT_LAST; k++) {
3935 efree(ARRAYG(multisort_flags)[k]);
3936 }
3937 efree(arrays);
3938 efree(args);
3939 RETURN_TRUE;
3940 }
3941 /* }}} */
3942
3943 /* {{{ proto mixed array_rand(array input [, int num_req])
3944 Return key/keys for random entry/entries in the array */
PHP_FUNCTION(array_rand)3945 PHP_FUNCTION(array_rand)
3946 {
3947 zval *input;
3948 long randval, num_req = 1;
3949 int num_avail, key_type;
3950 char *string_key;
3951 uint string_key_len;
3952 ulong num_key;
3953 HashPosition pos;
3954
3955 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) {
3956 return;
3957 }
3958
3959 num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
3960
3961 if (ZEND_NUM_ARGS() > 1) {
3962 if (num_req <= 0 || num_req > num_avail) {
3963 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
3964 return;
3965 }
3966 }
3967
3968 /* Make the return value an array only if we need to pass back more than one result. */
3969 if (num_req > 1) {
3970 array_init_size(return_value, num_req);
3971 }
3972
3973 /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
3974 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
3975 while (num_req && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
3976
3977 randval = php_rand(TSRMLS_C);
3978
3979 if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
3980 /* If we are returning a single result, just do it. */
3981 if (Z_TYPE_P(return_value) != IS_ARRAY) {
3982 if (key_type == HASH_KEY_IS_STRING) {
3983 RETURN_STRINGL(string_key, string_key_len - 1, 1);
3984 } else {
3985 RETURN_LONG(num_key);
3986 }
3987 } else {
3988 /* Append the result to the return value. */
3989 if (key_type == HASH_KEY_IS_STRING) {
3990 add_next_index_stringl(return_value, string_key, string_key_len - 1, 1);
3991 } else {
3992 add_next_index_long(return_value, num_key);
3993 }
3994 }
3995 num_req--;
3996 }
3997 num_avail--;
3998 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
3999 }
4000 }
4001 /* }}} */
4002
4003 /* {{{ proto mixed array_sum(array input)
4004 Returns the sum of the array entries */
PHP_FUNCTION(array_sum)4005 PHP_FUNCTION(array_sum)
4006 {
4007 zval *input,
4008 **entry,
4009 entry_n;
4010 HashPosition pos;
4011 double dval;
4012
4013 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
4014 return;
4015 }
4016
4017 ZVAL_LONG(return_value, 0);
4018
4019 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
4020 zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
4021 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
4022 ) {
4023 if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
4024 continue;
4025 }
4026 entry_n = **entry;
4027 zval_copy_ctor(&entry_n);
4028 convert_scalar_to_number(&entry_n TSRMLS_CC);
4029
4030 if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
4031 dval = (double)Z_LVAL_P(return_value) + (double)Z_LVAL(entry_n);
4032 if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) {
4033 Z_LVAL_P(return_value) += Z_LVAL(entry_n);
4034 continue;
4035 }
4036 }
4037 convert_to_double(return_value);
4038 convert_to_double(&entry_n);
4039 Z_DVAL_P(return_value) += Z_DVAL(entry_n);
4040 }
4041 }
4042 /* }}} */
4043
4044 /* {{{ proto mixed array_product(array input)
4045 Returns the product of the array entries */
PHP_FUNCTION(array_product)4046 PHP_FUNCTION(array_product)
4047 {
4048 zval *input,
4049 **entry,
4050 entry_n;
4051 HashPosition pos;
4052 double dval;
4053
4054 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
4055 return;
4056 }
4057
4058 ZVAL_LONG(return_value, 1);
4059 if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
4060 return;
4061 }
4062
4063 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
4064 zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
4065 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
4066 ) {
4067 if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
4068 continue;
4069 }
4070 entry_n = **entry;
4071 zval_copy_ctor(&entry_n);
4072 convert_scalar_to_number(&entry_n TSRMLS_CC);
4073
4074 if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
4075 dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
4076 if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) {
4077 Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
4078 continue;
4079 }
4080 }
4081 convert_to_double(return_value);
4082 convert_to_double(&entry_n);
4083 Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
4084 }
4085 }
4086 /* }}} */
4087
4088 /* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
4089 Iteratively reduce the array to a single value via the callback. */
PHP_FUNCTION(array_reduce)4090 PHP_FUNCTION(array_reduce)
4091 {
4092 zval *input;
4093 zval **args[2];
4094 zval **operand;
4095 zval *result = NULL;
4096 zval *retval;
4097 zend_fcall_info fci;
4098 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4099 zval *initial = NULL;
4100 HashPosition pos;
4101 HashTable *htbl;
4102
4103 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
4104 return;
4105 }
4106
4107 if (ZEND_NUM_ARGS() > 2) {
4108 ALLOC_ZVAL(result);
4109 MAKE_COPY_ZVAL(&initial, result);
4110 } else {
4111 MAKE_STD_ZVAL(result);
4112 ZVAL_NULL(result);
4113 }
4114
4115 /* (zval **)input points to an element of argument stack
4116 * the base pointer of which is subject to change.
4117 * thus we need to keep the pointer to the hashtable for safety */
4118 htbl = Z_ARRVAL_P(input);
4119
4120 if (zend_hash_num_elements(htbl) == 0) {
4121 if (result) {
4122 RETVAL_ZVAL(result, 1, 1);
4123 }
4124 return;
4125 }
4126
4127 fci.retval_ptr_ptr = &retval;
4128 fci.param_count = 2;
4129 fci.no_separation = 0;
4130
4131 zend_hash_internal_pointer_reset_ex(htbl, &pos);
4132 while (zend_hash_get_current_data_ex(htbl, (void **)&operand, &pos) == SUCCESS) {
4133
4134 if (result) {
4135 args[0] = &result;
4136 args[1] = operand;
4137 fci.params = args;
4138
4139 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
4140 zval_ptr_dtor(&result);
4141 result = retval;
4142 } else {
4143 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback");
4144 return;
4145 }
4146 } else {
4147 result = *operand;
4148 zval_add_ref(&result);
4149 }
4150 zend_hash_move_forward_ex(htbl, &pos);
4151 }
4152 RETVAL_ZVAL(result, 1, 1);
4153 }
4154 /* }}} */
4155
4156 /* {{{ proto array array_filter(array input [, mixed callback])
4157 Filters elements from the array via the callback. */
PHP_FUNCTION(array_filter)4158 PHP_FUNCTION(array_filter)
4159 {
4160 zval *array;
4161 zval **operand;
4162 zval **args[1];
4163 zval *retval = NULL;
4164 zend_bool have_callback = 0;
4165 char *string_key;
4166 zend_fcall_info fci = empty_fcall_info;
4167 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4168 uint string_key_len;
4169 ulong num_key;
4170 HashPosition pos;
4171
4172 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|f", &array, &fci, &fci_cache) == FAILURE) {
4173 return;
4174 }
4175
4176 array_init(return_value);
4177 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
4178 return;
4179 }
4180
4181 if (ZEND_NUM_ARGS() > 1) {
4182 have_callback = 1;
4183 fci.no_separation = 0;
4184 fci.retval_ptr_ptr = &retval;
4185 fci.param_count = 1;
4186 }
4187
4188 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
4189 zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&operand, &pos) == SUCCESS;
4190 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
4191 ) {
4192 if (have_callback) {
4193 args[0] = operand;
4194 fci.params = args;
4195
4196 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
4197 if (!zend_is_true(retval)) {
4198 zval_ptr_dtor(&retval);
4199 continue;
4200 } else {
4201 zval_ptr_dtor(&retval);
4202 }
4203 } else {
4204 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
4205 return;
4206 }
4207 } else if (!zend_is_true(*operand)) {
4208 continue;
4209 }
4210
4211 zval_add_ref(operand);
4212 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos)) {
4213 case HASH_KEY_IS_STRING:
4214 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
4215 break;
4216
4217 case HASH_KEY_IS_LONG:
4218 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand, sizeof(zval *), NULL);
4219 break;
4220 }
4221 }
4222 }
4223 /* }}} */
4224
4225 /* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
4226 Applies the callback to the elements in given arrays. */
PHP_FUNCTION(array_map)4227 PHP_FUNCTION(array_map)
4228 {
4229 zval ***arrays = NULL;
4230 int n_arrays = 0;
4231 zval ***params;
4232 zval *result, *null;
4233 HashPosition *array_pos;
4234 zval **args;
4235 zend_fcall_info fci = empty_fcall_info;
4236 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4237 int i, k, maxlen = 0;
4238 int *array_len;
4239
4240 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) {
4241 return;
4242 }
4243
4244 RETVAL_NULL();
4245
4246 args = (zval **)safe_emalloc(n_arrays, sizeof(zval *), 0);
4247 array_len = (int *)safe_emalloc(n_arrays, sizeof(int), 0);
4248 array_pos = (HashPosition *)safe_emalloc(n_arrays, sizeof(HashPosition), 0);
4249
4250 for (i = 0; i < n_arrays; i++) {
4251 if (Z_TYPE_PP(arrays[i]) != IS_ARRAY) {
4252 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2);
4253 efree(arrays);
4254 efree(args);
4255 efree(array_len);
4256 efree(array_pos);
4257 return;
4258 }
4259 SEPARATE_ZVAL_IF_NOT_REF(arrays[i]);
4260 args[i] = *arrays[i];
4261 array_len[i] = zend_hash_num_elements(Z_ARRVAL_PP(arrays[i]));
4262 if (array_len[i] > maxlen) {
4263 maxlen = array_len[i];
4264 }
4265 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(arrays[i]), &array_pos[i]);
4266 }
4267
4268 efree(arrays);
4269
4270 /* Short-circuit: if no callback and only one array, just return it. */
4271 if (!ZEND_FCI_INITIALIZED(fci) && n_arrays == 1) {
4272 RETVAL_ZVAL(args[0], 1, 0);
4273 efree(array_len);
4274 efree(array_pos);
4275 efree(args);
4276 return;
4277 }
4278
4279 array_init_size(return_value, maxlen);
4280 params = (zval ***)safe_emalloc(n_arrays, sizeof(zval **), 0);
4281 MAKE_STD_ZVAL(null);
4282 ZVAL_NULL(null);
4283
4284 /* We iterate through all the arrays at once. */
4285 for (k = 0; k < maxlen; k++) {
4286 uint str_key_len;
4287 ulong num_key;
4288 char *str_key;
4289 int key_type = 0;
4290
4291 /* If no callback, the result will be an array, consisting of current
4292 * entries from all arrays. */
4293 if (!ZEND_FCI_INITIALIZED(fci)) {
4294 MAKE_STD_ZVAL(result);
4295 array_init_size(result, n_arrays);
4296 }
4297
4298 for (i = 0; i < n_arrays; i++) {
4299 /* If this array still has elements, add the current one to the
4300 * parameter list, otherwise use null value. */
4301 if (k < array_len[i]) {
4302 zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), (void **)¶ms[i], &array_pos[i]);
4303
4304 /* It is safe to store only last value of key type, because
4305 * this loop will run just once if there is only 1 array. */
4306 if (n_arrays == 1) {
4307 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(args[0]), &str_key, &str_key_len, &num_key, 0, &array_pos[i]);
4308 }
4309 zend_hash_move_forward_ex(Z_ARRVAL_P(args[i]), &array_pos[i]);
4310 } else {
4311 params[i] = &null;
4312 }
4313
4314 if (!ZEND_FCI_INITIALIZED(fci)) {
4315 zval_add_ref(params[i]);
4316 add_next_index_zval(result, *params[i]);
4317 }
4318 }
4319
4320 if (ZEND_FCI_INITIALIZED(fci)) {
4321 fci.retval_ptr_ptr = &result;
4322 fci.param_count = n_arrays;
4323 fci.params = params;
4324 fci.no_separation = 0;
4325
4326 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || !result) {
4327 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
4328 efree(array_len);
4329 efree(args);
4330 efree(array_pos);
4331 zval_dtor(return_value);
4332 zval_ptr_dtor(&null);
4333 efree(params);
4334 RETURN_NULL();
4335 }
4336 }
4337
4338 if (n_arrays > 1) {
4339 add_next_index_zval(return_value, result);
4340 } else {
4341 if (key_type == HASH_KEY_IS_STRING) {
4342 add_assoc_zval_ex(return_value, str_key, str_key_len, result);
4343 } else {
4344 add_index_zval(return_value, num_key, result);
4345 }
4346 }
4347 }
4348
4349 zval_ptr_dtor(&null);
4350 efree(params);
4351 efree(array_len);
4352 efree(array_pos);
4353 efree(args);
4354 }
4355 /* }}} */
4356
4357 /* {{{ proto bool array_key_exists(mixed key, array search)
4358 Checks if the given key or index exists in the array */
PHP_FUNCTION(array_key_exists)4359 PHP_FUNCTION(array_key_exists)
4360 {
4361 zval *key; /* key to check for */
4362 HashTable *array; /* array to check in */
4363
4364 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zH", &key, &array) == FAILURE) {
4365 return;
4366 }
4367
4368 switch (Z_TYPE_P(key)) {
4369 case IS_STRING:
4370 if (zend_symtable_exists(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1)) {
4371 RETURN_TRUE;
4372 }
4373 RETURN_FALSE;
4374 case IS_LONG:
4375 if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
4376 RETURN_TRUE;
4377 }
4378 RETURN_FALSE;
4379 case IS_NULL:
4380 if (zend_hash_exists(array, "", 1)) {
4381 RETURN_TRUE;
4382 }
4383 RETURN_FALSE;
4384
4385 default:
4386 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
4387 RETURN_FALSE;
4388 }
4389 }
4390 /* }}} */
4391
4392 /* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
4393 Split array into chunks */
PHP_FUNCTION(array_chunk)4394 PHP_FUNCTION(array_chunk)
4395 {
4396 int argc = ZEND_NUM_ARGS(), key_type, num_in;
4397 long size, current = 0;
4398 char *str_key;
4399 uint str_key_len;
4400 ulong num_key;
4401 zend_bool preserve_keys = 0;
4402 zval *input = NULL;
4403 zval *chunk = NULL;
4404 zval **entry;
4405 HashPosition pos;
4406
4407 if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) {
4408 return;
4409 }
4410 /* Do bounds checking for size parameter. */
4411 if (size < 1) {
4412 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0");
4413 return;
4414 }
4415
4416 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
4417
4418 if (size > num_in) {
4419 size = num_in > 0 ? num_in : 1;
4420 }
4421
4422 array_init_size(return_value, ((num_in - 1) / size) + 1);
4423
4424 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
4425 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&entry, &pos) == SUCCESS) {
4426 /* If new chunk, create and initialize it. */
4427 if (!chunk) {
4428 MAKE_STD_ZVAL(chunk);
4429 array_init_size(chunk, size);
4430 }
4431
4432 /* Add entry to the chunk, preserving keys if necessary. */
4433 zval_add_ref(entry);
4434
4435 if (preserve_keys) {
4436 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &str_key, &str_key_len, &num_key, 0, &pos);
4437 switch (key_type) {
4438 case HASH_KEY_IS_STRING:
4439 add_assoc_zval_ex(chunk, str_key, str_key_len, *entry);
4440 break;
4441 default:
4442 add_index_zval(chunk, num_key, *entry);
4443 break;
4444 }
4445 } else {
4446 add_next_index_zval(chunk, *entry);
4447 }
4448
4449 /* If reached the chunk size, add it to the result array, and reset the
4450 * pointer. */
4451 if (!(++current % size)) {
4452 add_next_index_zval(return_value, chunk);
4453 chunk = NULL;
4454 }
4455
4456 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
4457 }
4458
4459 /* Add the final chunk if there is one. */
4460 if (chunk) {
4461 add_next_index_zval(return_value, chunk);
4462 }
4463 }
4464 /* }}} */
4465
4466 /* {{{ proto array array_combine(array keys, array values)
4467 Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
PHP_FUNCTION(array_combine)4468 PHP_FUNCTION(array_combine)
4469 {
4470 zval *values, *keys;
4471 HashPosition pos_values, pos_keys;
4472 zval **entry_keys, **entry_values;
4473 int num_keys, num_values;
4474
4475 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) {
4476 return;
4477 }
4478
4479 num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys));
4480 num_values = zend_hash_num_elements(Z_ARRVAL_P(values));
4481
4482 if (num_keys != num_values) {
4483 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements");
4484 RETURN_FALSE;
4485 }
4486
4487 if (!num_keys) {
4488 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have at least 1 element");
4489 RETURN_FALSE;
4490 }
4491
4492 array_init_size(return_value, num_keys);
4493
4494 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos_keys);
4495 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
4496 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry_keys, &pos_keys) == SUCCESS &&
4497 zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&entry_values, &pos_values) == SUCCESS
4498 ) {
4499 if (Z_TYPE_PP(entry_keys) == IS_LONG) {
4500 zval_add_ref(entry_values);
4501 add_index_zval(return_value, Z_LVAL_PP(entry_keys), *entry_values);
4502 } else {
4503 zval key, *key_ptr = *entry_keys;
4504
4505 if (Z_TYPE_PP(entry_keys) != IS_STRING) {
4506 key = **entry_keys;
4507 zval_copy_ctor(&key);
4508 convert_to_string(&key);
4509 key_ptr = &key;
4510 }
4511
4512 zval_add_ref(entry_values);
4513 add_assoc_zval_ex(return_value, Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, *entry_values);
4514
4515 if (key_ptr != *entry_keys) {
4516 zval_dtor(&key);
4517 }
4518 }
4519
4520 zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos_keys);
4521 zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
4522 }
4523 }
4524 /* }}} */
4525
4526 /*
4527 * Local variables:
4528 * tab-width: 4
4529 * c-basic-offset: 4
4530 * End:
4531 * vim600: noet sw=4 ts=4 fdm=marker
4532 * vim<600: noet sw=4 ts=4
4533 */
4534