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