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 (Z_OBJ_HT_P(array)->get_class_entry && 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/* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */
1996static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
1997{
1998    zval *stack,    /* Input stack */
1999         *val;      /* Value to be popped */
2000    zend_string *key = NULL;
2001    zend_ulong index;
2002
2003#ifndef FAST_ZPP
2004    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &stack) == FAILURE) {
2005        return;
2006    }
2007#else
2008    ZEND_PARSE_PARAMETERS_START(1, 1)
2009        Z_PARAM_ARRAY_EX(stack, 0, 1)
2010    ZEND_PARSE_PARAMETERS_END();
2011#endif
2012
2013    if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
2014        return;
2015    }
2016
2017    /* Get the first or last value and copy it into the return value */
2018    if (off_the_end) {
2019        zend_hash_internal_pointer_end(Z_ARRVAL_P(stack));
2020        while (1) {
2021            val = zend_hash_get_current_data(Z_ARRVAL_P(stack));
2022            if (!val) {
2023                return;
2024            } else if (Z_TYPE_P(val) == IS_INDIRECT) {
2025                val = Z_INDIRECT_P(val);
2026                if (Z_TYPE_P(val) == IS_UNDEF) {
2027                    zend_hash_move_backwards(Z_ARRVAL_P(stack));
2028                    continue;
2029                }
2030            }
2031            break;
2032        }
2033    } else {
2034        zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2035        while (1) {
2036            val = zend_hash_get_current_data(Z_ARRVAL_P(stack));
2037            if (!val) {
2038                return;
2039            } else if (Z_TYPE_P(val) == IS_INDIRECT) {
2040                val = Z_INDIRECT_P(val);
2041                if (Z_TYPE_P(val) == IS_UNDEF) {
2042                    zend_hash_move_forward(Z_ARRVAL_P(stack));
2043                    continue;
2044                }
2045            }
2046            break;
2047        }
2048    }
2049    RETVAL_ZVAL_FAST(val);
2050
2051    /* Delete the first or last value */
2052    zend_hash_get_current_key(Z_ARRVAL_P(stack), &key, &index, 0);
2053    if (key && Z_ARRVAL_P(stack) == &EG(symbol_table).ht) {
2054        zend_delete_global_variable(key TSRMLS_CC);
2055    } else if (key) {
2056        zend_hash_del(Z_ARRVAL_P(stack), key);
2057    } else {
2058        zend_hash_index_del(Z_ARRVAL_P(stack), index);
2059    }
2060
2061    /* If we did a shift... re-index like it did before */
2062    if (!off_the_end) {
2063        unsigned int k = 0;
2064        int should_rehash = 0;
2065        uint idx;
2066        Bucket *p;
2067
2068        for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
2069            p = Z_ARRVAL_P(stack)->arData + idx;
2070            if (Z_TYPE(p->val) == IS_UNDEF) continue;
2071            if (p->key == NULL) {
2072                if (p->h != k) {
2073                    p->h = k++;
2074                    should_rehash = 1;
2075                } else {
2076                    k++;
2077                }
2078            }
2079        }
2080        Z_ARRVAL_P(stack)->nNextFreeElement = k;
2081        if (should_rehash) {
2082            if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) {
2083                zend_hash_packed_to_hash(Z_ARRVAL_P(stack));
2084            } else {
2085                zend_hash_rehash(Z_ARRVAL_P(stack));
2086            }
2087        }
2088    } else if (!key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && index >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
2089        Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
2090    }
2091
2092    zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2093}
2094/* }}} */
2095
2096/* {{{ proto mixed array_pop(array stack)
2097   Pops an element off the end of the array */
2098PHP_FUNCTION(array_pop)
2099{
2100    _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2101}
2102/* }}} */
2103
2104/* {{{ proto mixed array_shift(array stack)
2105   Pops an element off the beginning of the array */
2106PHP_FUNCTION(array_shift)
2107{
2108    _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2109}
2110/* }}} */
2111
2112/* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
2113   Pushes elements onto the beginning of the array */
2114PHP_FUNCTION(array_unshift)
2115{
2116    zval   *args,           /* Function arguments array */
2117           *stack;          /* Input stack */
2118    HashTable *new_hash;    /* New hashtable for the stack */
2119    HashTable  old_hash;
2120    int argc;               /* Number of function arguments */
2121
2122    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/+", &stack, &args, &argc) == FAILURE) {
2123        return;
2124    }
2125
2126    /* Use splice to insert the elements at the beginning. Destroy old
2127     * hashtable and replace it with new one */
2128    new_hash = php_splice(Z_ARRVAL_P(stack), 0, 0, &args[0], argc, NULL);
2129    old_hash = *Z_ARRVAL_P(stack);
2130    *Z_ARRVAL_P(stack) = *new_hash;
2131    FREE_HASHTABLE(new_hash);
2132    zend_hash_destroy(&old_hash);
2133
2134    /* Clean up and return the number of elements in the stack */
2135    RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
2136}
2137/* }}} */
2138
2139/* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
2140   Removes the elements designated by offset and length and replace them with supplied array */
2141PHP_FUNCTION(array_splice)
2142{
2143    zval *array,                /* Input array */
2144         *repl_array = NULL,    /* Replacement array */
2145         *repl = NULL;          /* Replacement elements */
2146    HashTable *new_hash = NULL, /* Output array's hash */
2147              *rem_hash = NULL; /* Removed elements' hash */
2148    HashTable  old_hash;
2149    uint    idx;
2150    Bucket *p;                  /* Bucket used for traversing hash */
2151    zend_long   i,
2152            offset,
2153            length = 0,
2154            repl_num = 0;       /* Number of replacement elements */
2155    int     num_in;             /* Number of elements in the input array */
2156
2157    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/l|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
2158        return;
2159    }
2160
2161    num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
2162
2163    if (ZEND_NUM_ARGS() < 3) {
2164        length = num_in;
2165    }
2166
2167    if (ZEND_NUM_ARGS() == 4) {
2168        /* Make sure the last argument, if passed, is an array */
2169        convert_to_array(repl_array);
2170
2171        /* Create the array of replacement elements */
2172        repl_num = zend_hash_num_elements(Z_ARRVAL_P(repl_array));
2173        repl = (zval *)safe_emalloc(repl_num, sizeof(zval), 0);
2174        for (idx = 0, i = 0; idx < Z_ARRVAL_P(repl_array)->nNumUsed; idx++) {
2175            p = Z_ARRVAL_P(repl_array)->arData + idx;
2176            if (Z_TYPE(p->val) == IS_UNDEF) continue;
2177            ZVAL_COPY_VALUE(&repl[i++], &p->val);
2178        }
2179    }
2180
2181    /* Don't create the array of removed elements if it's not going
2182     * to be used; e.g. only removing and/or replacing elements */
2183    if (USED_RET()) {
2184        int size = length;
2185
2186        /* Clamp the offset.. */
2187        if (offset > num_in) {
2188            offset = num_in;
2189        } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2190            offset = 0;
2191        }
2192
2193        /* ..and the length */
2194        if (length < 0) {
2195            size = num_in - offset + length;
2196        } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
2197            size = num_in - offset;
2198        }
2199
2200        /* Initialize return value */
2201        array_init_size(return_value, size > 0 ? size : 0);
2202        rem_hash = Z_ARRVAL_P(return_value);
2203    }
2204
2205    /* Perform splice */
2206    new_hash = php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash);
2207
2208    /* Replace input array's hashtable with the new one */
2209    old_hash = *Z_ARRVAL_P(array);
2210    *Z_ARRVAL_P(array) = *new_hash;
2211    FREE_HASHTABLE(new_hash);
2212    zend_hash_destroy(&old_hash);
2213
2214    /* Clean up */
2215    if (ZEND_NUM_ARGS() == 4) {
2216        efree(repl);
2217    }
2218}
2219/* }}} */
2220
2221/* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
2222   Returns elements specified by offset and length */
2223PHP_FUNCTION(array_slice)
2224{
2225    zval     *input,        /* Input array */
2226             *z_length = NULL, /* How many elements to get */
2227             *entry;        /* An array entry */
2228    zend_long    offset,        /* Offset to get elements from */
2229             length = 0;
2230    zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */
2231    int      num_in,        /* Number of elements in the input array */
2232             pos;           /* Current position in the array */
2233    zend_string *string_key;
2234    zend_ulong num_key;
2235
2236#ifndef FAST_ZPP
2237    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
2238        return;
2239    }
2240#else
2241    ZEND_PARSE_PARAMETERS_START(2, 4)
2242        Z_PARAM_ARRAY(input)
2243        Z_PARAM_LONG(offset)
2244        Z_PARAM_OPTIONAL
2245        Z_PARAM_ZVAL(z_length)
2246        Z_PARAM_BOOL(preserve_keys)
2247    ZEND_PARSE_PARAMETERS_END();
2248#endif
2249
2250    /* Get number of entries in the input hash */
2251    num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
2252
2253    /* We want all entries from offset to the end if length is not passed or is null */
2254    if (ZEND_NUM_ARGS() < 3 || Z_TYPE_P(z_length) == IS_NULL) {
2255        length = num_in;
2256    } else {
2257        length = zval_get_long(z_length);
2258    }
2259
2260    /* Clamp the offset.. */
2261    if (offset > num_in) {
2262        array_init(return_value);
2263        return;
2264    } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2265        offset = 0;
2266    }
2267
2268    /* ..and the length */
2269    if (length < 0) {
2270        length = num_in - offset + length;
2271    } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
2272        length = num_in - offset;
2273    }
2274
2275    /* Initialize returned array */
2276    array_init_size(return_value, length > 0 ? length : 0);
2277
2278    if (length <= 0) {
2279        return;
2280    }
2281
2282    /* Start at the beginning and go until we hit offset */
2283    pos = 0;
2284    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
2285        pos++;
2286        if (pos <= offset) {
2287            continue;
2288        }
2289        if (pos > offset + length) {
2290            break;
2291        }
2292
2293        /* Copy elements from input array to the one that's returned */
2294        zval_add_ref(entry);
2295
2296        if (string_key) {
2297            zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
2298        } else {
2299            if (preserve_keys) {
2300                zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
2301            } else {
2302                zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
2303            }
2304        }
2305    } ZEND_HASH_FOREACH_END();
2306}
2307/* }}} */
2308
2309PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */
2310{
2311    zval *src_entry, *dest_entry;
2312    zend_string *string_key;
2313
2314    if (recursive) {
2315        ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
2316            if (string_key) {
2317                if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) {
2318                    zval *src_zval = src_entry;
2319                    zval *dest_zval = dest_entry;
2320                    HashTable *thash;
2321                    zval tmp;
2322                    int ret;
2323
2324                    ZVAL_DEREF(src_zval);
2325                    ZVAL_DEREF(dest_zval);
2326                    thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
2327                    if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
2328                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2329                        return 0;
2330                    }
2331
2332                    if (Z_ISREF_P(dest_entry)) {
2333                        if (Z_REFCOUNT_P(dest_entry) == 1) {
2334                            ZVAL_UNREF(dest_entry);
2335                        } else {
2336                            Z_DELREF_P(dest_entry);
2337                            ZVAL_DUP(dest_entry, dest_zval);
2338                        }
2339                        dest_zval = dest_entry;
2340                    } else {
2341                        SEPARATE_ZVAL(dest_zval);
2342                    }
2343
2344                    if (Z_TYPE_P(dest_zval) == IS_NULL) {
2345                        convert_to_array_ex(dest_zval);
2346                        add_next_index_null(dest_zval);
2347                    } else {
2348                        convert_to_array_ex(dest_zval);
2349                    }
2350                    ZVAL_UNDEF(&tmp);
2351                    if (Z_TYPE_P(src_zval) == IS_OBJECT) {
2352                        ZVAL_DUP(&tmp, src_zval);
2353                        convert_to_array(&tmp);
2354                        src_zval = &tmp;
2355                    }
2356                    if (Z_TYPE_P(src_zval) == IS_ARRAY) {
2357                        if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
2358                            thash->u.v.nApplyCount++;
2359                        }
2360                        ret = php_array_merge(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval), 1 TSRMLS_CC);
2361                        if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
2362                            thash->u.v.nApplyCount--;
2363                        }
2364                        if (!ret) {
2365                            return 0;
2366                        }
2367                    } else {
2368                        if (Z_REFCOUNTED_P(src_entry)) {
2369                            Z_ADDREF_P(src_entry);
2370                        }
2371                        zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
2372                    }
2373                    zval_ptr_dtor(&tmp);
2374                } else {
2375                    if (Z_REFCOUNTED_P(src_entry)) {
2376                        Z_ADDREF_P(src_entry);
2377                    }
2378                    zend_hash_add_new(dest, string_key, src_entry);
2379                }
2380            } else {
2381                if (Z_REFCOUNTED_P(src_entry)) {
2382                    Z_ADDREF_P(src_entry);
2383                }
2384                zend_hash_next_index_insert_new(dest, src_entry);
2385            }
2386        } ZEND_HASH_FOREACH_END();
2387    } else {
2388        ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
2389            if (string_key) {
2390                if (Z_REFCOUNTED_P(src_entry)) {
2391                    Z_ADDREF_P(src_entry);
2392                }
2393                zend_hash_update(dest, string_key, src_entry);
2394            } else {
2395                if (Z_REFCOUNTED_P(src_entry)) {
2396                    Z_ADDREF_P(src_entry);
2397                }
2398                zend_hash_next_index_insert_new(dest, src_entry);
2399            }
2400        } ZEND_HASH_FOREACH_END();
2401    }
2402    return 1;
2403}
2404/* }}} */
2405
2406PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */
2407{
2408    zval *src_entry, *dest_entry, *src_zval, *dest_zval;
2409    zend_string *string_key;
2410    zend_ulong num_key;
2411    int ret;
2412
2413    ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
2414        src_zval = src_entry;
2415        ZVAL_DEREF(src_zval);
2416        if (string_key) {
2417            if (Z_TYPE_P(src_zval) != IS_ARRAY ||
2418                (dest_entry = zend_hash_find(dest, string_key)) == NULL ||
2419                (Z_TYPE_P(dest_entry) != IS_ARRAY &&
2420                 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
2421
2422                if (Z_REFCOUNTED_P(src_entry)) {
2423                    Z_ADDREF_P(src_entry);
2424                }
2425                zend_hash_update(dest, string_key, src_entry);
2426
2427                continue;
2428            }
2429        } else {
2430            if (Z_TYPE_P(src_zval) != IS_ARRAY ||
2431                (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
2432                (Z_TYPE_P(dest_entry) != IS_ARRAY &&
2433                 (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
2434
2435                if (Z_REFCOUNTED_P(src_entry)) {
2436                    Z_ADDREF_P(src_entry);
2437                }
2438                zend_hash_index_update(dest, num_key, src_entry);
2439
2440                continue;
2441            }
2442        }
2443
2444        dest_zval = dest_entry;
2445        ZVAL_DEREF(dest_zval);
2446        if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 ||
2447            Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 ||
2448            (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))) {
2449            php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2450            return 0;
2451        }
2452        SEPARATE_ZVAL(dest_zval);
2453
2454        if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
2455            Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++;
2456        }
2457        if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
2458            Z_ARRVAL_P(src_zval)->u.v.nApplyCount++;
2459        }
2460
2461        ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval) TSRMLS_CC);
2462
2463        if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
2464            Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--;
2465        }
2466        if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
2467            Z_ARRVAL_P(src_zval)->u.v.nApplyCount--;
2468        }
2469
2470        if (!ret) {
2471            return 0;
2472        }
2473    } ZEND_HASH_FOREACH_END();
2474
2475    return 1;
2476}
2477/* }}} */
2478
2479static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
2480{
2481    zval *args = NULL;
2482    int argc, i, init_size = 0;
2483
2484#ifndef FAST_ZPP
2485    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
2486        return;
2487    }
2488#else
2489    ZEND_PARSE_PARAMETERS_START(1, -1)
2490        Z_PARAM_VARIADIC('+', args, argc)
2491    ZEND_PARSE_PARAMETERS_END();
2492#endif
2493
2494    for (i = 0; i < argc; i++) {
2495        zval *arg = args + i;
2496
2497        ZVAL_DEREF(arg);
2498        if (Z_TYPE_P(arg) != IS_ARRAY) {
2499            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
2500            RETURN_NULL();
2501        } else {
2502            int num = zend_hash_num_elements(Z_ARRVAL_P(arg));
2503
2504            if (num > init_size) {
2505                init_size = num;
2506            }
2507        }
2508    }
2509
2510    array_init_size(return_value, init_size);
2511
2512    for (i = 0; i < argc; i++) {
2513        zval *arg = args + i;
2514
2515        ZVAL_DEREF(arg);
2516        if (!replace) {
2517            php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), recursive TSRMLS_CC);
2518        } else if (recursive && i > 0) { /* First array will be copied directly instead */
2519            php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg) TSRMLS_CC);
2520        } else {
2521            zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1);
2522        }
2523    }
2524}
2525/* }}} */
2526
2527/* {{{ proto array array_merge(array arr1, array arr2 [, array ...])
2528   Merges elements from passed arrays into one array */
2529PHP_FUNCTION(array_merge)
2530{
2531    php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
2532}
2533/* }}} */
2534
2535/* {{{ proto array array_merge_recursive(array arr1, array arr2 [, array ...])
2536   Recursively merges elements from passed arrays into one array */
2537PHP_FUNCTION(array_merge_recursive)
2538{
2539    php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
2540}
2541/* }}} */
2542
2543/* {{{ proto array array_replace(array arr1, array arr2 [, array ...])
2544   Replaces elements from passed arrays into one array */
2545PHP_FUNCTION(array_replace)
2546{
2547    php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
2548}
2549/* }}} */
2550
2551/* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...])
2552   Recursively replaces elements from passed arrays into one array */
2553PHP_FUNCTION(array_replace_recursive)
2554{
2555    php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
2556}
2557/* }}} */
2558
2559/* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
2560   Return just the keys from the input array, optionally only for the specified search_value */
2561PHP_FUNCTION(array_keys)
2562{
2563    zval *input,                /* Input array */
2564         *search_value = NULL,  /* Value to search for */
2565         *entry,                /* An entry in the input array */
2566           res,                 /* Result of comparison */
2567           new_val;             /* New value */
2568    int    add_key;             /* Flag to indicate whether a key should be added */
2569    zend_bool strict = 0;       /* do strict comparison */
2570    zend_ulong num_idx;
2571    zend_string *str_idx;
2572    int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
2573
2574#ifndef FAST_ZPP
2575    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
2576        return;
2577    }
2578#else
2579    ZEND_PARSE_PARAMETERS_START(1, 3)
2580        Z_PARAM_ARRAY(input)
2581        Z_PARAM_OPTIONAL
2582        Z_PARAM_ZVAL(search_value)
2583        Z_PARAM_BOOL(strict)
2584    ZEND_PARSE_PARAMETERS_END();
2585#endif
2586
2587    if (strict) {
2588        is_equal_func = is_identical_function;
2589    }
2590
2591    /* Initialize return array */
2592    if (search_value != NULL) {
2593        array_init(return_value);
2594    } else {
2595        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2596    }
2597    add_key = 1;
2598
2599    /* Go through input array and add keys to the return array */
2600    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
2601        if (search_value != NULL) {
2602            is_equal_func(&res, search_value, entry TSRMLS_CC);
2603            add_key = zval_is_true(&res);
2604        }
2605
2606        if (add_key) {
2607            if (str_idx) {
2608                ZVAL_STR_COPY(&new_val, str_idx);
2609            } else {
2610                ZVAL_LONG(&new_val, num_idx);
2611            }
2612            zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
2613        }
2614    } ZEND_HASH_FOREACH_END();
2615}
2616/* }}} */
2617
2618/* {{{ proto array array_values(array input)
2619   Return just the values from the input array */
2620PHP_FUNCTION(array_values)
2621{
2622    zval     *input,        /* Input array */
2623             *entry;        /* An entry in the input array */
2624
2625    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2626        return;
2627    }
2628
2629    /* Initialize return array */
2630    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2631
2632    /* Go through input array and add values to the return array */
2633    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
2634        zval_add_ref(entry);
2635        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
2636    } ZEND_HASH_FOREACH_END();
2637}
2638/* }}} */
2639
2640/* {{{ proto array array_count_values(array input)
2641   Return the value as key and the frequency of that value in input as value */
2642PHP_FUNCTION(array_count_values)
2643{
2644    zval    *input,     /* Input array */
2645            *entry,     /* An entry in the input array */
2646            *tmp;
2647    HashTable *myht;
2648
2649    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2650        return;
2651    }
2652
2653    /* Initialize return array */
2654    array_init(return_value);
2655
2656    /* Go through input array and add values to the return array */
2657    myht = Z_ARRVAL_P(input);
2658    ZEND_HASH_FOREACH_VAL(myht, entry) {
2659        if (Z_TYPE_P(entry) == IS_LONG) {
2660            if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
2661                zval data;
2662                ZVAL_LONG(&data, 1);
2663                zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
2664            } else {
2665                Z_LVAL_P(tmp)++;
2666            }
2667        } else if (Z_TYPE_P(entry) == IS_STRING) {
2668            if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
2669                zval data;
2670                ZVAL_LONG(&data, 1);
2671                zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2672            } else {
2673                Z_LVAL_P(tmp)++;
2674            }
2675        } else {
2676            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
2677        }
2678    } ZEND_HASH_FOREACH_END();
2679}
2680/* }}} */
2681
2682/* {{{ array_column_param_helper
2683 * Specialized conversion rules for array_column() function
2684 */
2685static inline
2686zend_bool array_column_param_helper(zval *param,
2687                                    const char *name TSRMLS_DC) {
2688    switch (Z_TYPE_P(param)) {
2689        case IS_DOUBLE:
2690            convert_to_long_ex(param);
2691            /* fallthrough */
2692        case IS_LONG:
2693            return 1;
2694
2695        case IS_OBJECT:
2696            convert_to_string_ex(param);
2697            /* fallthrough */
2698        case IS_STRING:
2699            return 1;
2700
2701        default:
2702            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The %s key should be either a string or an integer", name);
2703            return 0;
2704    }
2705}
2706
2707/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
2708   Return the values from a single column in the input array, identified by the
2709   value_key and optionally indexed by the index_key */
2710PHP_FUNCTION(array_column)
2711{
2712    zval *zcolumn = NULL, *zkey = NULL, *data;
2713    HashTable *arr_hash;
2714    zval *zcolval = NULL, *zkeyval = NULL;
2715    HashTable *ht;
2716
2717    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
2718        return;
2719    }
2720
2721    if ((zcolumn && !array_column_param_helper(zcolumn, "column" TSRMLS_CC)) ||
2722        (zkey && !array_column_param_helper(zkey, "index" TSRMLS_CC))) {
2723        RETURN_FALSE;
2724    }
2725
2726    array_init(return_value);
2727    ZEND_HASH_FOREACH_VAL(arr_hash, data) {
2728        if (Z_TYPE_P(data) != IS_ARRAY) {
2729            /* Skip elemens which are not sub-arrays */
2730            continue;
2731        }
2732        ht = Z_ARRVAL_P(data);
2733
2734        if (!zcolumn) {
2735            /* NULL column ID means use entire subarray as data */
2736            zcolval = data;
2737
2738            /* Otherwise, skip if the value doesn't exist in our subarray */
2739        } else if ((Z_TYPE_P(zcolumn) == IS_STRING) &&
2740            ((zcolval = zend_hash_find(ht, Z_STR_P(zcolumn))) == NULL)) {
2741            continue;
2742        } else if ((Z_TYPE_P(zcolumn) == IS_LONG) &&
2743            ((zcolval = zend_hash_index_find(ht, Z_LVAL_P(zcolumn))) == NULL)) {
2744            continue;
2745        }
2746
2747        /* Failure will leave zkeyval alone which will land us on the final else block below
2748         * which is to append the value as next_index
2749         */
2750        if (zkey && (Z_TYPE_P(zkey) == IS_STRING)) {
2751            zkeyval = zend_hash_find(ht, Z_STR_P(zkey));
2752        } else if (zkey && (Z_TYPE_P(zkey) == IS_LONG)) {
2753            zkeyval = zend_hash_index_find(ht, Z_LVAL_P(zkey));
2754        }
2755
2756        if (Z_REFCOUNTED_P(zcolval)) {
2757            Z_ADDREF_P(zcolval);
2758        }
2759        if (zkeyval && Z_TYPE_P(zkeyval) == IS_STRING) {
2760            zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
2761        } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_LONG) {
2762            add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval);
2763        } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_OBJECT) {
2764            SEPARATE_ZVAL(zkeyval);
2765            convert_to_string(zkeyval);
2766            zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
2767        } else {
2768            add_next_index_zval(return_value, zcolval);
2769        }
2770    } ZEND_HASH_FOREACH_END();
2771}
2772/* }}} */
2773
2774/* {{{ proto array array_reverse(array input [, bool preserve keys])
2775   Return input as a new array with the order of the entries reversed */
2776PHP_FUNCTION(array_reverse)
2777{
2778    zval     *input,                /* Input array */
2779             *entry;                /* An entry in the input array */
2780    zend_string *string_key;
2781    zend_ulong    num_key;
2782    zend_bool preserve_keys = 0;    /* whether to preserve keys */
2783
2784    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &input, &preserve_keys) == FAILURE) {
2785        return;
2786    }
2787
2788    /* Initialize return array */
2789    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2790
2791    ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
2792        zval_add_ref(entry);
2793
2794        if (string_key) {
2795            zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
2796        } else {
2797            if (preserve_keys) {
2798                zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
2799            } else {
2800                zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
2801            }
2802        }
2803    } ZEND_HASH_FOREACH_END();
2804}
2805/* }}} */
2806
2807/* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
2808   Returns a copy of input array padded with pad_value to size pad_size */
2809PHP_FUNCTION(array_pad)
2810{
2811    zval  *input;       /* Input array */
2812    zval  *pad_value;   /* Padding value obviously */
2813    zval  *pads;        /* Array to pass to splice */
2814    HashTable *new_hash;/* Return value from splice */
2815    HashTable  old_hash;
2816    zend_long pad_size;     /* Size to pad to */
2817    zend_long pad_size_abs; /* Absolute value of pad_size */
2818    zend_long input_size;       /* Size of the input array */
2819    zend_long num_pads;     /* How many pads do we need */
2820    int do_pad;         /* Whether we should do padding at all */
2821    int i;
2822
2823    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "alz", &input, &pad_size, &pad_value) == FAILURE) {
2824        return;
2825    }
2826
2827    /* Do some initial calculations */
2828    input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
2829    pad_size_abs = ZEND_ABS(pad_size);
2830    if (pad_size_abs < 0) {
2831        php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2832        zval_dtor(return_value);
2833        RETURN_FALSE;
2834    }
2835    do_pad = (input_size >= pad_size_abs) ? 0 : 1;
2836
2837    /* Copy the original array */
2838    RETVAL_ZVAL(input, 1, 0);
2839
2840    /* If no need to pad, no need to continue */
2841    if (!do_pad) {
2842        return;
2843    }
2844
2845    /* Populate the pads array */
2846    num_pads = pad_size_abs - input_size;
2847    if (num_pads > Z_L(1048576)) {
2848        php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2849        zval_dtor(return_value);
2850        RETURN_FALSE;
2851    }
2852    pads = (zval *)safe_emalloc(num_pads, sizeof(zval), 0);
2853    for (i = 0; i < num_pads; i++) {
2854        ZVAL_COPY_VALUE(&pads[i], pad_value);
2855    }
2856
2857    /* Pad on the right or on the left */
2858    if (pad_size > 0) {
2859        new_hash = php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL);
2860    } else {
2861        new_hash = php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL);
2862    }
2863
2864    /* Copy the result hash into return value */
2865    old_hash = *Z_ARRVAL_P(return_value);
2866    *Z_ARRVAL_P(return_value) = *new_hash;
2867    FREE_HASHTABLE(new_hash);
2868    zend_hash_destroy(&old_hash);
2869
2870    /* Clean up */
2871    efree(pads);
2872}
2873/* }}} */
2874
2875/* {{{ proto array array_flip(array input)
2876   Return array with key <-> value flipped */
2877PHP_FUNCTION(array_flip)
2878{
2879    zval *array, *entry, data;
2880    zend_ulong num_idx;
2881    zend_string *str_idx;
2882
2883    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2884        return;
2885    }
2886
2887    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2888
2889    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
2890        if (Z_TYPE_P(entry) == IS_LONG) {
2891            if (str_idx) {
2892                ZVAL_STR_COPY(&data, str_idx);
2893            } else {
2894                ZVAL_LONG(&data, num_idx);
2895            }
2896            zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
2897        } else if (Z_TYPE_P(entry) == IS_STRING) {
2898            if (str_idx) {
2899                ZVAL_STR_COPY(&data, str_idx);
2900            } else {
2901                ZVAL_LONG(&data, num_idx);
2902            }
2903            zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2904        } else {
2905            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
2906        }
2907    } ZEND_HASH_FOREACH_END();
2908}
2909/* }}} */
2910
2911/* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
2912   Retuns an array with all string keys lowercased [or uppercased] */
2913PHP_FUNCTION(array_change_key_case)
2914{
2915    zval *array, *entry;
2916    zend_string *string_key;
2917    zend_string *new_key;
2918    zend_ulong num_key;
2919    zend_long change_to_upper=0;
2920
2921    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &change_to_upper) == FAILURE) {
2922        return;
2923    }
2924
2925    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2926
2927    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
2928        zval_add_ref(entry);
2929
2930        if (!string_key) {
2931            zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
2932        } else {
2933            new_key = zend_string_init(string_key->val, string_key->len, 0);
2934            if (change_to_upper) {
2935                php_strtoupper(new_key->val, new_key->len);
2936            } else {
2937                php_strtolower(new_key->val, new_key->len);
2938            }
2939            zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
2940            zend_string_release(new_key);
2941        }
2942    } ZEND_HASH_FOREACH_END();
2943}
2944/* }}} */
2945
2946/* {{{ proto array array_unique(array input [, int sort_flags])
2947   Removes duplicate values from array */
2948PHP_FUNCTION(array_unique)
2949{
2950    zval *array;
2951    uint idx;
2952    Bucket *p;
2953    struct bucketindex {
2954        Bucket b;
2955        unsigned int i;
2956    };
2957    struct bucketindex *arTmp, *cmpdata, *lastkept;
2958    unsigned int i;
2959    zend_long sort_type = PHP_SORT_STRING;
2960
2961    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
2962        return;
2963    }
2964
2965    php_set_compare_func(sort_type TSRMLS_CC);
2966
2967    ZVAL_NEW_ARR(return_value);
2968    zend_array_dup(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array));
2969
2970    if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {   /* nothing to do */
2971        return;
2972    }
2973
2974    /* create and sort array with pointers to the target_hash buckets */
2975    arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
2976    if (!arTmp) {
2977        zval_dtor(return_value);
2978        RETURN_FALSE;
2979    }
2980    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) {
2981        p = Z_ARRVAL_P(array)->arData + idx;
2982        if (Z_TYPE(p->val) == IS_UNDEF) continue;
2983        if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue;
2984        arTmp[i].b = *p;
2985        arTmp[i].i = i;
2986        i++;
2987    }
2988    ZVAL_UNDEF(&arTmp[i].b.val);
2989    zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC);
2990
2991    /* go through the sorted array and delete duplicates from the copy */
2992    lastkept = arTmp;
2993    for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
2994        if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
2995            lastkept = cmpdata;
2996        } else {
2997            if (lastkept->i > cmpdata->i) {
2998                p = &lastkept->b;
2999                lastkept = cmpdata;
3000            } else {
3001                p = &cmpdata->b;
3002            }
3003            if (p->key == NULL) {
3004                zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3005            } else {
3006                if (Z_ARRVAL_P(return_value) == &EG(symbol_table).ht) {
3007                    zend_delete_global_variable(p->key TSRMLS_CC);
3008                } else {
3009                    zend_hash_del(Z_ARRVAL_P(return_value), p->key);
3010                }
3011            }
3012        }
3013    }
3014    pefree(arTmp, Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
3015}
3016/* }}} */
3017
3018static int zval_compare(zval *a, zval *b TSRMLS_DC) /* {{{ */
3019{
3020    zval result;
3021    zval *first;
3022    zval *second;
3023
3024    first = a;
3025    second = b;
3026
3027    if (Z_TYPE_P(first) == IS_INDIRECT) {
3028        first = Z_INDIRECT_P(first);
3029    }
3030    if (Z_TYPE_P(second) == IS_INDIRECT) {
3031        second = Z_INDIRECT_P(second);
3032    }
3033    if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
3034        return 0;
3035    }
3036
3037    if (Z_TYPE(result) == IS_DOUBLE) {
3038        return ZEND_NORMALIZE_BOOL(Z_DVAL(result));
3039    }
3040
3041    convert_to_long(&result);
3042    return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
3043}
3044/* }}} */
3045
3046static int zval_user_compare(zval *a, zval *b TSRMLS_DC) /* {{{ */
3047{
3048    zval args[2];
3049    zval retval;
3050
3051    if (Z_TYPE_P(a) == IS_INDIRECT) {
3052        a = Z_INDIRECT_P(a);
3053    }
3054    if (Z_TYPE_P(b) == IS_INDIRECT) {
3055        b = Z_INDIRECT_P(b);
3056    }
3057
3058    ZVAL_COPY_VALUE(&args[0], a);
3059    ZVAL_COPY_VALUE(&args[1], b);
3060
3061    BG(user_compare_fci).param_count = 2;
3062    BG(user_compare_fci).params = args;
3063    BG(user_compare_fci).retval = &retval;
3064    BG(user_compare_fci).no_separation = 0;
3065
3066    if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
3067        zend_long ret = zval_get_long(&retval);
3068        zval_ptr_dtor(&retval);
3069        return ret < 0 ? -1 : ret > 0 ? 1 : 0;;
3070    } else {
3071        return 0;
3072    }
3073}
3074/* }}} */
3075
3076static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
3077{
3078    uint idx;
3079    Bucket *p;
3080    int argc, i;
3081    zval *args;
3082    int (*intersect_data_compare_func)(zval *, zval * TSRMLS_DC) = NULL;
3083    zend_bool ok;
3084    zval *val, *data;
3085    int req_args;
3086    char *param_spec;
3087
3088    /* Get the argument count */
3089    argc = ZEND_NUM_ARGS();
3090    if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3091        /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
3092        req_args = 3;
3093        param_spec = "+f";
3094        intersect_data_compare_func = zval_user_compare;
3095    } else {
3096        /*  INTERSECT_COMP_DATA_NONE - array_intersect_key()
3097            INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
3098        req_args = 2;
3099        param_spec = "+";
3100
3101        if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
3102            intersect_data_compare_func = zval_compare;
3103        }
3104    }
3105
3106    if (argc < req_args) {
3107        php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
3108        return;
3109    }
3110
3111    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
3112        return;
3113    }
3114
3115    for (i = 0; i < argc; i++) {
3116        if (Z_TYPE(args[i]) != IS_ARRAY) {
3117            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3118            RETURN_NULL();
3119        }
3120    }
3121
3122    array_init(return_value);
3123
3124    for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
3125        p = Z_ARRVAL(args[0])->arData + idx;
3126        val = &p->val;
3127        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3128        if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
3129            ZVAL_UNREF(val);
3130        }
3131        if (p->key == NULL) {
3132            ok = 1;
3133            for (i = 1; i < argc; i++) {
3134                if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) == NULL ||
3135                    (intersect_data_compare_func &&
3136                    intersect_data_compare_func(val, data TSRMLS_CC) != 0)
3137                ) {
3138                    ok = 0;
3139                    break;
3140                }
3141            }
3142            if (ok) {
3143                if (Z_REFCOUNTED_P(val)) {
3144                    Z_ADDREF_P(val);
3145                }
3146                zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
3147            }
3148        } else {
3149            ok = 1;
3150            for (i = 1; i < argc; i++) {
3151                if ((data = zend_hash_find(Z_ARRVAL(args[i]), p->key)) == NULL ||
3152                    (intersect_data_compare_func &&
3153                    intersect_data_compare_func(val, data TSRMLS_CC) != 0)
3154                ) {
3155                    ok = 0;
3156                    break;
3157                }
3158            }
3159            if (ok) {
3160                if (Z_REFCOUNTED_P(val)) {
3161                    Z_ADDREF_P(val);
3162                }
3163                zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
3164            }
3165        }
3166    }
3167}
3168/* }}} */
3169
3170static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
3171{
3172    zval *args = NULL;
3173    HashTable *hash;
3174    int arr_argc, i, c = 0;
3175    uint idx;
3176    Bucket **lists, *list, **ptrs, *p;
3177    uint32_t req_args;
3178    char *param_spec;
3179    zend_fcall_info fci1, fci2;
3180    zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
3181    zend_fcall_info *fci_key = NULL, *fci_data;
3182    zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
3183    PHP_ARRAY_CMP_FUNC_VARS;
3184
3185    int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC);
3186    int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC);
3187
3188    if (behavior == INTERSECT_NORMAL) {
3189        intersect_key_compare_func = php_array_key_compare;
3190
3191        if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
3192            /* array_intersect() */
3193            req_args = 2;
3194            param_spec = "+";
3195            intersect_data_compare_func = php_array_data_compare;
3196        } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3197            /* array_uintersect() */
3198            req_args = 3;
3199            param_spec = "+f";
3200            intersect_data_compare_func = php_array_user_compare;
3201        } else {
3202            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);
3203            return;
3204        }
3205
3206        if (ZEND_NUM_ARGS() < req_args) {
3207            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3208            return;
3209        }
3210
3211        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3212            return;
3213        }
3214        fci_data = &fci1;
3215        fci_data_cache = &fci1_cache;
3216
3217    } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3218        /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
3219         * no comparison of the data is done (part of INTERSECT_ASSOC) */
3220        intersect_key_compare_func = php_array_key_compare;
3221
3222        if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
3223            /* array_intersect_assoc() or array_intersect_key() */
3224            req_args = 2;
3225            param_spec = "+";
3226            intersect_key_compare_func = php_array_key_compare;
3227            intersect_data_compare_func = php_array_data_compare;
3228        } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
3229            /* array_uintersect_assoc() */
3230            req_args = 3;
3231            param_spec = "+f";
3232            intersect_key_compare_func = php_array_key_compare;
3233            intersect_data_compare_func = php_array_user_compare;
3234            fci_data = &fci1;
3235            fci_data_cache = &fci1_cache;
3236        } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
3237            /* array_intersect_uassoc() or array_intersect_ukey() */
3238            req_args = 3;
3239            param_spec = "+f";
3240            intersect_key_compare_func = php_array_user_key_compare;
3241            intersect_data_compare_func = php_array_data_compare;
3242            fci_key = &fci1;
3243            fci_key_cache = &fci1_cache;
3244        } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
3245            /* array_uintersect_uassoc() */
3246            req_args = 4;
3247            param_spec = "+ff";
3248            intersect_key_compare_func = php_array_user_key_compare;
3249            intersect_data_compare_func = php_array_user_compare;
3250            fci_data = &fci1;
3251            fci_data_cache = &fci1_cache;
3252            fci_key = &fci2;
3253            fci_key_cache = &fci2_cache;
3254        } else {
3255            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);
3256            return;
3257        }
3258
3259        if (ZEND_NUM_ARGS() < req_args) {
3260            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3261            return;
3262        }
3263
3264        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
3265            return;
3266        }
3267
3268    } else {
3269        php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3270        return;
3271    }
3272
3273    PHP_ARRAY_CMP_FUNC_BACKUP();
3274
3275    /* for each argument, create and sort list with pointers to the hash buckets */
3276    lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
3277    ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
3278    php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3279
3280    if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
3281        BG(user_compare_fci) = *fci_data;
3282        BG(user_compare_fci_cache) = *fci_data_cache;
3283    } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
3284        BG(user_compare_fci) = *fci_key;
3285        BG(user_compare_fci_cache) = *fci_key_cache;
3286    }
3287
3288    for (i = 0; i < arr_argc; i++) {
3289        if (Z_TYPE(args[i]) != IS_ARRAY) {
3290            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3291            arr_argc = i; /* only free up to i - 1 */
3292            goto out;
3293        }
3294        hash = Z_ARRVAL(args[i]);
3295        list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
3296        if (!list) {
3297            PHP_ARRAY_CMP_FUNC_RESTORE();
3298
3299            efree(ptrs);
3300            efree(lists);
3301            RETURN_FALSE;
3302        }
3303        lists[i] = list;
3304        ptrs[i] = list;
3305        for (idx = 0; idx < hash->nNumUsed; idx++) {
3306            p = hash->arData + idx;
3307            if (Z_TYPE(p->val) == IS_UNDEF) continue;
3308            *list++ = *p;
3309        }
3310        ZVAL_UNDEF(&list->val);
3311        if (behavior == INTERSECT_NORMAL) {
3312            zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket), intersect_data_compare_func TSRMLS_CC);
3313        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3314            zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket), intersect_key_compare_func TSRMLS_CC);
3315        }
3316    }
3317
3318    /* copy the argument array */
3319    RETVAL_ZVAL(&args[0], 1, 0);
3320    if (Z_ARRVAL_P(return_value) == &EG(symbol_table).ht) {
3321        HashTable *old_ht = Z_ARRVAL_P(return_value);
3322
3323        ZVAL_NEW_ARR(return_value);
3324        zend_array_dup(Z_ARRVAL_P(return_value), old_ht);
3325    }
3326
3327    /* go through the lists and look for common values */
3328    while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
3329        if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
3330            &&
3331            key_compare_type == INTERSECT_COMP_KEY_USER) {
3332
3333            BG(user_compare_fci) = *fci_key;
3334            BG(user_compare_fci_cache) = *fci_key_cache;
3335        }
3336
3337        for (i = 1; i < arr_argc; i++) {
3338            if (behavior & INTERSECT_NORMAL) {
3339                while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3340                    ptrs[i]++;
3341                }
3342            } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3343                while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3344                    ptrs[i]++;
3345                }
3346                if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
3347                    /* this means that ptrs[i] is not NULL so we can compare
3348                     * and "c==0" is from last operation
3349                     * in this branch of code we enter only when INTERSECT_ASSOC
3350                     * since when we have INTERSECT_KEY compare of data is not wanted. */
3351                    if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3352                        BG(user_compare_fci) = *fci_data;
3353                        BG(user_compare_fci_cache) = *fci_data_cache;
3354                    }
3355                    if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) {
3356                        c = 1;
3357                        if (key_compare_type == INTERSECT_COMP_KEY_USER) {
3358                            BG(user_compare_fci) = *fci_key;
3359                            BG(user_compare_fci_cache) = *fci_key_cache;
3360                            /* When KEY_USER, the last parameter is always the callback */
3361                        }
3362                        /* we are going to the break */
3363                    } else {
3364                        /* continue looping */
3365                    }
3366                }
3367            }
3368            if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
3369                /* delete any values corresponding to remains of ptrs[0] */
3370                /* and exit because they do not present in at least one of */
3371                /* the other arguments */
3372                for (;;) {
3373                    p = ptrs[0]++;
3374                    if (Z_TYPE(p->val) == IS_UNDEF) {
3375                        goto out;
3376                    }
3377                    if (p->key == NULL) {
3378                        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3379                    } else {
3380                        zend_hash_del(Z_ARRVAL_P(return_value), p->key);
3381                    }
3382                }
3383            }
3384            if (c) /* here we get if not all are equal */
3385                break;
3386            ptrs[i]++;
3387        }
3388        if (c) {
3389            /* Value of ptrs[0] not in all arguments, delete all entries */
3390            /* with value < value of ptrs[i] */
3391            for (;;) {
3392                p = ptrs[0];
3393                if (p->key == NULL) {
3394                    zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3395                } else {
3396                    zend_hash_del(Z_ARRVAL_P(return_value), p->key);
3397                }
3398                if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
3399                    goto out;
3400                }
3401                if (behavior == INTERSECT_NORMAL) {
3402                    if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) {
3403                        break;
3404                    }
3405                } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3406                    /* no need of looping because indexes are unique */
3407                    break;
3408                }
3409            }
3410        } else {
3411            /* ptrs[0] is present in all the arguments */
3412            /* Skip all entries with same value as ptrs[0] */
3413            for (;;) {
3414                if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
3415                    goto out;
3416                }
3417                if (behavior == INTERSECT_NORMAL) {
3418                    if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3419                        break;
3420                    }
3421                } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3422                    /* no need of looping because indexes are unique */
3423                    break;
3424                }
3425            }
3426        }
3427    }
3428out:
3429    for (i = 0; i < arr_argc; i++) {
3430        hash = Z_ARRVAL(args[i]);
3431        pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
3432    }
3433
3434    PHP_ARRAY_CMP_FUNC_RESTORE();
3435
3436    efree(ptrs);
3437    efree(lists);
3438}
3439/* }}} */
3440
3441/* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
3442   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. */
3443PHP_FUNCTION(array_intersect_key)
3444{
3445    php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
3446}
3447/* }}} */
3448
3449/* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
3450   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. */
3451PHP_FUNCTION(array_intersect_ukey)
3452{
3453    php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3454}
3455/* }}} */
3456
3457/* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
3458   Returns the entries of arr1 that have values which are present in all the other arguments */
3459PHP_FUNCTION(array_intersect)
3460{
3461    php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
3462}
3463/* }}} */
3464
3465/* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
3466   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. */
3467PHP_FUNCTION(array_uintersect)
3468{
3469    php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
3470}
3471/* }}} */
3472
3473/* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
3474   Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
3475PHP_FUNCTION(array_intersect_assoc)
3476{
3477    php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
3478}
3479/* }}} */
3480
3481/* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
3482   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. */
3483PHP_FUNCTION(array_intersect_uassoc)
3484{
3485    php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3486}
3487/* }}} */
3488
3489/* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
3490   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. */
3491PHP_FUNCTION(array_uintersect_assoc)
3492{
3493    php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
3494}
3495/* }}} */
3496
3497/* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
3498   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. */
3499PHP_FUNCTION(array_uintersect_uassoc)
3500{
3501    php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
3502}
3503/* }}} */
3504
3505static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
3506{
3507    uint idx;
3508    Bucket *p;
3509    int argc, i;
3510    zval *args;
3511    int (*diff_data_compare_func)(zval *, zval * TSRMLS_DC) = NULL;
3512    zend_bool ok;
3513    zval *val, *data;
3514
3515    /* Get the argument count */
3516    argc = ZEND_NUM_ARGS();
3517    if (data_compare_type == DIFF_COMP_DATA_USER) {
3518        if (argc < 3) {
3519            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
3520            return;
3521        }
3522        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
3523            return;
3524        }
3525        diff_data_compare_func = zval_user_compare;
3526    } else {
3527        if (argc < 2) {
3528            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
3529            return;
3530        }
3531        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3532            return;
3533        }
3534        if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3535            diff_data_compare_func = zval_compare;
3536        }
3537    }
3538
3539    for (i = 0; i < argc; i++) {
3540        if (Z_TYPE(args[i]) != IS_ARRAY) {
3541            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3542            RETURN_NULL();
3543        }
3544    }
3545
3546    array_init(return_value);
3547
3548    for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
3549        p = Z_ARRVAL(args[0])->arData + idx;
3550        val = &p->val;
3551        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3552        if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
3553            ZVAL_UNREF(val);
3554        }
3555        if (p->key == NULL) {
3556            ok = 1;
3557            for (i = 1; i < argc; i++) {
3558                if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) != NULL &&
3559                    (!diff_data_compare_func ||
3560                    diff_data_compare_func(val, data TSRMLS_CC) == 0)
3561                ) {
3562                    ok = 0;
3563                    break;
3564                }
3565            }
3566            if (ok) {
3567                if (Z_REFCOUNTED_P(val)) {
3568                    Z_ADDREF_P(val);
3569                }
3570                zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
3571            }
3572        } else {
3573            ok = 1;
3574            for (i = 1; i < argc; i++) {
3575                if ((data = zend_hash_find(Z_ARRVAL(args[i]), p->key)) != NULL &&
3576                    (!diff_data_compare_func ||
3577                    diff_data_compare_func(val, data TSRMLS_CC) == 0)
3578                ) {
3579                    ok = 0;
3580                    break;
3581                }
3582            }
3583            if (ok) {
3584                if (Z_REFCOUNTED_P(val)) {
3585                    Z_ADDREF_P(val);
3586                }
3587                zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
3588            }
3589        }
3590    }
3591}
3592/* }}} */
3593
3594static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
3595{
3596    zval *args = NULL;
3597    HashTable *hash;
3598    int arr_argc, i, c;
3599    uint idx;
3600    Bucket **lists, *list, **ptrs, *p;
3601    uint32_t req_args;
3602    char *param_spec;
3603    zend_fcall_info fci1, fci2;
3604    zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
3605    zend_fcall_info *fci_key = NULL, *fci_data;
3606    zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
3607    PHP_ARRAY_CMP_FUNC_VARS;
3608
3609    int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC);
3610    int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC);
3611
3612    if (behavior == DIFF_NORMAL) {
3613        diff_key_compare_func = php_array_key_compare;
3614
3615        if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3616            /* array_diff */
3617            req_args = 2;
3618            param_spec = "+";
3619            diff_data_compare_func = php_array_data_compare;
3620        } else if (data_compare_type == DIFF_COMP_DATA_USER) {
3621            /* array_udiff */
3622            req_args = 3;
3623            param_spec = "+f";
3624            diff_data_compare_func = php_array_user_compare;
3625        } else {
3626            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);
3627            return;
3628        }
3629
3630        if (ZEND_NUM_ARGS() < req_args) {
3631            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3632            return;
3633        }
3634
3635        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3636            return;
3637        }
3638        fci_data = &fci1;
3639        fci_data_cache = &fci1_cache;
3640
3641    } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
3642        /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
3643         * no comparison of the data is done (part of DIFF_ASSOC) */
3644
3645        if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3646            /* array_diff_assoc() or array_diff_key() */
3647            req_args = 2;
3648            param_spec = "+";
3649            diff_key_compare_func = php_array_key_compare;
3650            diff_data_compare_func = php_array_data_compare;
3651        } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3652            /* array_udiff_assoc() */
3653            req_args = 3;
3654            param_spec = "+f";
3655            diff_key_compare_func = php_array_key_compare;
3656            diff_data_compare_func = php_array_user_compare;
3657            fci_data = &fci1;
3658            fci_data_cache = &fci1_cache;
3659        } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
3660            /* array_diff_uassoc() or array_diff_ukey() */
3661            req_args = 3;
3662            param_spec = "+f";
3663            diff_key_compare_func = php_array_user_key_compare;
3664            diff_data_compare_func = php_array_data_compare;
3665            fci_key = &fci1;
3666            fci_key_cache = &fci1_cache;
3667        } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
3668            /* array_udiff_uassoc() */
3669            req_args = 4;
3670            param_spec = "+ff";
3671            diff_key_compare_func = php_array_user_key_compare;
3672            diff_data_compare_func = php_array_user_compare;
3673            fci_data = &fci1;
3674            fci_data_cache = &fci1_cache;
3675            fci_key = &fci2;
3676            fci_key_cache = &fci2_cache;
3677        } else {
3678            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);
3679            return;
3680        }
3681
3682        if (ZEND_NUM_ARGS() < req_args) {
3683            php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3684            return;
3685        }
3686
3687        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
3688            return;
3689        }
3690
3691    } else {
3692        php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3693        return;
3694    }
3695
3696    PHP_ARRAY_CMP_FUNC_BACKUP();
3697
3698    /* for each argument, create and sort list with pointers to the hash buckets */
3699    lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
3700    ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
3701    php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3702
3703    if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
3704        BG(user_compare_fci) = *fci_data;
3705        BG(user_compare_fci_cache) = *fci_data_cache;
3706    } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
3707        BG(user_compare_fci) = *fci_key;
3708        BG(user_compare_fci_cache) = *fci_key_cache;
3709    }
3710
3711    for (i = 0; i < arr_argc; i++) {
3712        if (Z_TYPE(args[i]) != IS_ARRAY) {
3713            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3714            arr_argc = i; /* only free up to i - 1 */
3715            goto out;
3716        }
3717        hash = Z_ARRVAL(args[i]);
3718        list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
3719        if (!list) {
3720            PHP_ARRAY_CMP_FUNC_RESTORE();
3721
3722            efree(ptrs);
3723            efree(lists);
3724            RETURN_FALSE;
3725        }
3726        lists[i] = list;
3727        ptrs[i] = list;
3728        for (idx = 0; idx < hash->nNumUsed; idx++) {
3729            p = hash->arData + idx;
3730            if (Z_TYPE(p->val) == IS_UNDEF) continue;
3731            *list++ = *p;
3732        }
3733        ZVAL_UNDEF(&list->val);
3734        if (behavior == DIFF_NORMAL) {
3735            zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket), diff_data_compare_func TSRMLS_CC);
3736        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3737            zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket), diff_key_compare_func TSRMLS_CC);
3738        }
3739    }
3740
3741    /* copy the argument array */
3742    RETVAL_ZVAL(&args[0], 1, 0);
3743    if (Z_ARRVAL_P(return_value) == &EG(symbol_table).ht) {
3744        HashTable *old_ht = Z_ARRVAL_P(return_value);
3745
3746        ZVAL_NEW_ARR(return_value);
3747        zend_array_dup(Z_ARRVAL_P(return_value), old_ht);
3748    }
3749
3750    /* go through the lists and look for values of ptr[0] that are not in the others */
3751    while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
3752        if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
3753            &&
3754            key_compare_type == DIFF_COMP_KEY_USER
3755        ) {
3756            BG(user_compare_fci) = *fci_key;
3757            BG(user_compare_fci_cache) = *fci_key_cache;
3758        }
3759        c = 1;
3760        for (i = 1; i < arr_argc; i++) {
3761            Bucket *ptr = ptrs[i];
3762            if (behavior == DIFF_NORMAL) {
3763                while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3764                    ptrs[i]++;
3765                }
3766            } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3767                while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
3768                    ptr++;
3769                }
3770            }
3771            if (!c) {
3772                if (behavior == DIFF_NORMAL) {
3773                    if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
3774                        ptrs[i]++;
3775                    }
3776                    break;
3777                } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
3778                    /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
3779                     * data comparison is not needed - skipped. */
3780                    if (Z_TYPE(ptr->val) != IS_UNDEF) {
3781                        if (data_compare_type == DIFF_COMP_DATA_USER) {
3782                            BG(user_compare_fci) = *fci_data;
3783                            BG(user_compare_fci_cache) = *fci_data_cache;
3784                        }
3785                        if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) {
3786                            /* the data is not the same */
3787                            c = -1;
3788                            if (key_compare_type == DIFF_COMP_KEY_USER) {
3789                                BG(user_compare_fci) = *fci_key;
3790                                BG(user_compare_fci_cache) = *fci_key_cache;
3791                            }
3792                        } else {
3793                            break;
3794                            /* we have found the element in other arrays thus we don't want it
3795                             * in the return_value -> delete from there */
3796                        }
3797                    }
3798                } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
3799                    /* the behavior here differs from INTERSECT_KEY in php_intersect
3800                     * since in the "diff" case we have to remove the entry from
3801                     * return_value while when doing intersection the entry must not
3802                     * be deleted. */
3803                    break; /* remove the key */
3804                }
3805            }
3806        }
3807        if (!c) {
3808            /* ptrs[0] in one of the other arguments */
3809            /* delete all entries with value as ptrs[0] */
3810            for (;;) {
3811                p = ptrs[0];
3812                if (p->key == NULL) {
3813                    zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3814                } else {
3815                    zend_hash_del(Z_ARRVAL_P(return_value), p->key);
3816                }
3817                if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
3818                    goto out;
3819                }
3820                if (behavior == DIFF_NORMAL) {
3821                    if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3822                        break;
3823                    }
3824                } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3825                    /* in this case no array_key_compare is needed */
3826                    break;
3827                }
3828            }
3829        } else {
3830            /* ptrs[0] in none of the other arguments */
3831            /* skip all entries with value as ptrs[0] */
3832            for (;;) {
3833                if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
3834                    goto out;
3835                }
3836                if (behavior == DIFF_NORMAL) {
3837                    if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3838                        break;
3839                    }
3840                } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3841                    /* in this case no array_key_compare is needed */
3842                    break;
3843                }
3844            }
3845        }
3846    }
3847out:
3848    for (i = 0; i < arr_argc; i++) {
3849        hash = Z_ARRVAL(args[i]);
3850        pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
3851    }
3852
3853    PHP_ARRAY_CMP_FUNC_RESTORE();
3854
3855    efree(ptrs);
3856    efree(lists);
3857}
3858/* }}} */
3859
3860/* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
3861   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. */
3862PHP_FUNCTION(array_diff_key)
3863{
3864    php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
3865}
3866/* }}} */
3867
3868/* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
3869   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. */
3870PHP_FUNCTION(array_diff_ukey)
3871{
3872    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3873}
3874/* }}} */
3875
3876/* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
3877   Returns the entries of arr1 that have values which are not present in any of the others arguments. */
3878PHP_FUNCTION(array_diff)
3879{
3880    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
3881}
3882/* }}} */
3883
3884/* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
3885   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. */
3886PHP_FUNCTION(array_udiff)
3887{
3888    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
3889}
3890/* }}} */
3891
3892/* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
3893   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 */
3894PHP_FUNCTION(array_diff_assoc)
3895{
3896    php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
3897}
3898/* }}} */
3899
3900/* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
3901   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. */
3902PHP_FUNCTION(array_diff_uassoc)
3903{
3904    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3905}
3906/* }}} */
3907
3908/* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
3909   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. */
3910PHP_FUNCTION(array_udiff_assoc)
3911{
3912    php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
3913}
3914/* }}} */
3915
3916/* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
3917   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. */
3918PHP_FUNCTION(array_udiff_uassoc)
3919{
3920    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
3921}
3922/* }}} */
3923
3924#define MULTISORT_ORDER 0
3925#define MULTISORT_TYPE  1
3926#define MULTISORT_LAST  2
3927
3928PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
3929{
3930    Bucket *ab = *(Bucket **)a;
3931    Bucket *bb = *(Bucket **)b;
3932    int r;
3933    int result = 0;
3934    zval temp;
3935
3936    r = 0;
3937    do {
3938        php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC);
3939
3940        ARRAYG(compare_func)(&temp, &ab[r].val, &bb[r].val TSRMLS_CC);
3941        result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp);
3942        if (result != 0) {
3943            return result;
3944        }
3945        r++;
3946    } while (Z_TYPE(ab[r].val) != IS_UNDEF);
3947
3948    return result;
3949}
3950/* }}} */
3951
3952#define MULTISORT_ABORT                     \
3953    for (k = 0; k < MULTISORT_LAST; k++)    \
3954        efree(ARRAYG(multisort_flags)[k]);  \
3955    efree(arrays);                          \
3956    RETURN_FALSE;
3957
3958/* {{{ 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]], ...])
3959   Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
3960PHP_FUNCTION(array_multisort)
3961{
3962    zval*           args;
3963    zval**          arrays;
3964    Bucket**        indirect;
3965    uint            idx;
3966    Bucket*         p;
3967    HashTable*      hash;
3968    int             argc;
3969    int             array_size;
3970    int             num_arrays = 0;
3971    int             parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
3972    int             sort_order = PHP_SORT_ASC;
3973    int             sort_type  = PHP_SORT_REGULAR;
3974    int             i, k, n;
3975
3976    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3977        return;
3978    }
3979
3980    /* Allocate space for storing pointers to input arrays and sort flags. */
3981    arrays = (zval **)ecalloc(argc, sizeof(zval *));
3982    for (i = 0; i < MULTISORT_LAST; i++) {
3983        parse_state[i] = 0;
3984        ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
3985    }
3986
3987    /* Here we go through the input arguments and parse them. Each one can
3988     * be either an array or a sort flag which follows an array. If not
3989     * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
3990     * accordingly. There can't be two sort flags of the same type after an
3991     * array, and the very first argument has to be an array. */
3992    for (i = 0; i < argc; i++) {
3993        zval *arg = &args[i];
3994
3995        ZVAL_DEREF(arg);
3996        if (Z_TYPE_P(arg) == IS_ARRAY) {
3997            if (Z_IMMUTABLE_P(arg)) {
3998                zval_copy_ctor(arg);
3999            }
4000            /* We see the next array, so we update the sort flags of
4001             * the previous array and reset the sort flags. */
4002            if (i > 0) {
4003                ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
4004                ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
4005                sort_order = PHP_SORT_ASC;
4006                sort_type = PHP_SORT_REGULAR;
4007            }
4008            arrays[num_arrays++] = arg;
4009
4010            /* Next one may be an array or a list of sort flags. */
4011            for (k = 0; k < MULTISORT_LAST; k++) {
4012                parse_state[k] = 1;
4013            }
4014        } else if (Z_TYPE_P(arg) == IS_LONG) {
4015            switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
4016                case PHP_SORT_ASC:
4017                case PHP_SORT_DESC:
4018                    /* flag allowed here */
4019                    if (parse_state[MULTISORT_ORDER] == 1) {
4020                        /* Save the flag and make sure then next arg is not the current flag. */
4021                        sort_order = Z_LVAL(args[i]) == PHP_SORT_DESC ? -1 : 1;
4022                        parse_state[MULTISORT_ORDER] = 0;
4023                    } else {
4024                        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);
4025                        MULTISORT_ABORT;
4026                    }
4027                    break;
4028
4029                case PHP_SORT_REGULAR:
4030                case PHP_SORT_NUMERIC:
4031                case PHP_SORT_STRING:
4032                case PHP_SORT_NATURAL:
4033#if HAVE_STRCOLL
4034                case PHP_SORT_LOCALE_STRING:
4035#endif
4036                    /* flag allowed here */
4037                    if (parse_state[MULTISORT_TYPE] == 1) {
4038                        /* Save the flag and make sure then next arg is not the current flag. */
4039                        sort_type = Z_LVAL(args[i]);
4040                        parse_state[MULTISORT_TYPE] = 0;
4041                    } else {
4042                        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);
4043                        MULTISORT_ABORT;
4044                    }
4045                    break;
4046
4047                default:
4048                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
4049                    MULTISORT_ABORT;
4050                    break;
4051
4052            }
4053        } else {
4054            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
4055            MULTISORT_ABORT;
4056        }
4057    }
4058    /* Take care of the last array sort flags. */
4059    ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
4060    ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
4061
4062    /* Make sure the arrays are of the same size. */
4063    array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
4064    for (i = 0; i < num_arrays; i++) {
4065        if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
4066            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
4067            MULTISORT_ABORT;
4068        }
4069    }
4070
4071    /* If all arrays are empty we don't need to do anything. */
4072    if (array_size < 1) {
4073        for (k = 0; k < MULTISORT_LAST; k++) {
4074            efree(ARRAYG(multisort_flags)[k]);
4075        }
4076        efree(arrays);
4077        RETURN_TRUE;
4078    }
4079
4080    /* Create the indirection array. This array is of size MxN, where
4081     * M is the number of entries in each input array and N is the number
4082     * of the input arrays + 1. The last column is NULL to indicate the end
4083     * of the row. */
4084    indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
4085    for (i = 0; i < array_size; i++) {
4086        indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
4087    }
4088    for (i = 0; i < num_arrays; i++) {
4089        k = 0;
4090        for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
4091            p = Z_ARRVAL_P(arrays[i])->arData + idx;
4092            if (Z_TYPE(p->val) == IS_UNDEF) continue;
4093            indirect[k][i] = *p;
4094            k++;
4095        }
4096    }
4097    for (k = 0; k < array_size; k++) {
4098        ZVAL_UNDEF(&indirect[k][num_arrays].val);
4099    }
4100
4101    /* Do the actual sort magic - bada-bim, bada-boom. */
4102    zend_qsort(indirect, array_size, sizeof(Bucket *), php_multisort_compare TSRMLS_CC);
4103
4104    /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
4105    HANDLE_BLOCK_INTERRUPTIONS();
4106    for (i = 0; i < num_arrays; i++) {
4107        int repack;
4108
4109        hash = Z_ARRVAL_P(arrays[i]);
4110        hash->nNumUsed = array_size;
4111        hash->nInternalPointer = 0;
4112        repack = !(hash->u.flags & HASH_FLAG_PACKED);
4113
4114        for (n = 0, k = 0; k < array_size; k++) {
4115            hash->arData[k] = indirect[k][i];
4116            if (hash->arData[k].key == NULL) {
4117                hash->arData[k].h = n++;
4118            } else {
4119                repack = 0;
4120            }
4121        }
4122        hash->nNextFreeElement = array_size;
4123        if (repack) {
4124            zend_hash_to_packed(hash);
4125        }
4126    }
4127    HANDLE_UNBLOCK_INTERRUPTIONS();
4128
4129    /* Clean up. */
4130    for (i = 0; i < array_size; i++) {
4131        efree(indirect[i]);
4132    }
4133    efree(indirect);
4134    for (k = 0; k < MULTISORT_LAST; k++) {
4135        efree(ARRAYG(multisort_flags)[k]);
4136    }
4137    efree(arrays);
4138    RETURN_TRUE;
4139}
4140/* }}} */
4141
4142/* {{{ proto mixed array_rand(array input [, int num_req])
4143   Return key/keys for random entry/entries in the array */
4144PHP_FUNCTION(array_rand)
4145{
4146    zval *input;
4147    zend_long randval, num_req = 1;
4148    int num_avail;
4149    zend_string *string_key;
4150    zend_ulong num_key;
4151
4152    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) {
4153        return;
4154    }
4155
4156    num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
4157
4158    if (ZEND_NUM_ARGS() > 1) {
4159        if (num_req <= 0 || num_req > num_avail) {
4160            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
4161            return;
4162        }
4163    }
4164
4165    /* Make the return value an array only if we need to pass back more than one result. */
4166    if (num_req > 1) {
4167        array_init_size(return_value, num_req);
4168    }
4169
4170    /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
4171    ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
4172        if (!num_req) {
4173            break;
4174        }
4175
4176        randval = php_rand(TSRMLS_C);
4177
4178        if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
4179            /* If we are returning a single result, just do it. */
4180            if (Z_TYPE_P(return_value) != IS_ARRAY) {
4181                if (string_key) {
4182                    RETURN_STR(zend_string_copy(string_key));
4183                } else {
4184                    RETURN_LONG(num_key);
4185                }
4186            } else {
4187                /* Append the result to the return value. */
4188                if (string_key) {
4189                    add_next_index_str(return_value, zend_string_copy(string_key));
4190                } else {
4191                    add_next_index_long(return_value, num_key);
4192                }
4193            }
4194            num_req--;
4195        }
4196        num_avail--;
4197    } ZEND_HASH_FOREACH_END();
4198}
4199/* }}} */
4200
4201/* {{{ proto mixed array_sum(array input)
4202   Returns the sum of the array entries */
4203PHP_FUNCTION(array_sum)
4204{
4205    zval *input,
4206         *entry,
4207         entry_n;
4208
4209    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
4210        return;
4211    }
4212
4213    ZVAL_LONG(return_value, 0);
4214
4215    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4216        if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
4217            continue;
4218        }
4219        ZVAL_DUP(&entry_n, entry);
4220        convert_scalar_to_number(&entry_n TSRMLS_CC);
4221        fast_add_function(return_value, return_value, &entry_n TSRMLS_CC);
4222    } ZEND_HASH_FOREACH_END();
4223}
4224/* }}} */
4225
4226/* {{{ proto mixed array_product(array input)
4227   Returns the product of the array entries */
4228PHP_FUNCTION(array_product)
4229{
4230    zval *input,
4231         *entry,
4232         entry_n;
4233    double dval;
4234
4235    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
4236        return;
4237    }
4238
4239    ZVAL_LONG(return_value, 1);
4240    if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
4241        return;
4242    }
4243
4244    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4245        if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
4246            continue;
4247        }
4248        ZVAL_DUP(&entry_n, entry);
4249        convert_scalar_to_number(&entry_n TSRMLS_CC);
4250
4251        if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
4252            dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
4253            if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
4254                Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
4255                continue;
4256            }
4257        }
4258        convert_to_double(return_value);
4259        convert_to_double(&entry_n);
4260        Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
4261    } ZEND_HASH_FOREACH_END();
4262}
4263/* }}} */
4264
4265/* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
4266   Iteratively reduce the array to a single value via the callback. */
4267PHP_FUNCTION(array_reduce)
4268{
4269    zval *input;
4270    zval args[2];
4271    zval *operand;
4272    zval result;
4273    zval retval;
4274    zend_fcall_info fci;
4275    zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4276    zval *initial = NULL;
4277    HashTable *htbl;
4278
4279    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
4280        return;
4281    }
4282
4283
4284    if (ZEND_NUM_ARGS() > 2) {
4285        ZVAL_DUP(&result, initial);
4286    } else {
4287        ZVAL_NULL(&result);
4288    }
4289
4290    /* (zval **)input points to an element of argument stack
4291     * the base pointer of which is subject to change.
4292     * thus we need to keep the pointer to the hashtable for safety */
4293    htbl = Z_ARRVAL_P(input);
4294
4295    if (zend_hash_num_elements(htbl) == 0) {
4296        RETURN_ZVAL(&result, 1, 1);
4297    }
4298
4299    fci.retval = &retval;
4300    fci.param_count = 2;
4301    fci.no_separation = 0;
4302
4303    ZEND_HASH_FOREACH_VAL(htbl, operand) {
4304        ZVAL_COPY(&args[0], &result);
4305        ZVAL_COPY(&args[1], operand);
4306        fci.params = args;
4307
4308        if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
4309            zval_ptr_dtor(&args[1]);
4310            zval_ptr_dtor(&args[0]);
4311            zval_ptr_dtor(&result);
4312            ZVAL_COPY_VALUE(&result, &retval);
4313        } else {
4314            zval_ptr_dtor(&args[1]);
4315            zval_ptr_dtor(&args[0]);
4316            php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback");
4317            return;
4318        }
4319    } ZEND_HASH_FOREACH_END();
4320
4321    RETVAL_ZVAL(&result, 1, 1);
4322}
4323/* }}} */
4324
4325/* {{{ proto array array_filter(array input [, mixed callback])
4326   Filters elements from the array via the callback. */
4327PHP_FUNCTION(array_filter)
4328{
4329    zval *array;
4330    zval *operand;
4331    zval args[2];
4332    zval retval;
4333    zend_bool have_callback = 0;
4334    zend_long use_type = 0;
4335    zend_string *string_key;
4336    zend_fcall_info fci = empty_fcall_info;
4337    zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4338    zend_ulong num_key;
4339
4340    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
4341        return;
4342    }
4343
4344    array_init(return_value);
4345    if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
4346        return;
4347    }
4348
4349    if (ZEND_NUM_ARGS() > 1) {
4350        have_callback = 1;
4351        fci.no_separation = 0;
4352        fci.retval = &retval;
4353        fci.param_count = 1;
4354    }
4355
4356    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
4357        if (have_callback) {
4358            if (use_type) {
4359                /* Set up the key */
4360                if (!string_key) {
4361                    if (use_type == ARRAY_FILTER_USE_BOTH) {
4362                        fci.param_count = 2;
4363                        ZVAL_LONG(&args[1], num_key);
4364                    } else if (use_type == ARRAY_FILTER_USE_KEY) {
4365                        ZVAL_LONG(&args[0], num_key);
4366                    }
4367                } else {
4368                    if (use_type == ARRAY_FILTER_USE_BOTH) {
4369                        ZVAL_STR_COPY(&args[1], string_key);
4370                    } else if (use_type == ARRAY_FILTER_USE_KEY) {
4371                        ZVAL_STR_COPY(&args[0], string_key);
4372                    }
4373                }
4374            }
4375            if (use_type != ARRAY_FILTER_USE_KEY) {
4376                ZVAL_COPY(&args[0], operand);
4377            }
4378            fci.params = args;
4379
4380            if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS) {
4381                zval_ptr_dtor(&args[0]);
4382                if (use_type == ARRAY_FILTER_USE_BOTH) {
4383                    zval_ptr_dtor(&args[1]);
4384                }
4385                if (!Z_ISUNDEF(retval)) {
4386                    int retval_true = zend_is_true(&retval TSRMLS_CC);
4387
4388                    zval_ptr_dtor(&retval);
4389                    if (!retval_true) {
4390                        continue;
4391                    }
4392                } else {
4393                    continue;
4394                }
4395            } else {
4396                zval_ptr_dtor(&args[0]);
4397                if (use_type == ARRAY_FILTER_USE_BOTH) {
4398                    zval_ptr_dtor(&args[1]);
4399                }
4400                php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
4401                return;
4402            }
4403        } else if (!zend_is_true(operand TSRMLS_CC)) {
4404            continue;
4405        }
4406
4407        zval_add_ref(operand);
4408        if (string_key) {
4409            zend_hash_update(Z_ARRVAL_P(return_value), string_key, operand);
4410        } else {
4411            zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand);
4412        }
4413    } ZEND_HASH_FOREACH_END();
4414}
4415/* }}} */
4416
4417/* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
4418   Applies the callback to the elements in given arrays. */
4419PHP_FUNCTION(array_map)
4420{
4421    zval *arrays = NULL;
4422    int n_arrays = 0;
4423    zval result;
4424    zend_fcall_info fci = empty_fcall_info;
4425    zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4426    int i;
4427    uint32_t k, maxlen = 0;
4428
4429#ifndef FAST_ZPP
4430    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) {
4431        return;
4432    }
4433#else
4434    ZEND_PARSE_PARAMETERS_START(2, -1)
4435        Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
4436        Z_PARAM_VARIADIC('+', arrays, n_arrays)
4437    ZEND_PARSE_PARAMETERS_END();
4438#endif
4439
4440    RETVAL_NULL();
4441
4442    if (n_arrays == 1) {
4443        zend_ulong num_key;
4444        zend_string *str_key;
4445        zval *zv, arg;
4446
4447        if (Z_TYPE(arrays[0]) != IS_ARRAY) {
4448            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", 2);
4449            return;
4450        }
4451        maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
4452
4453        /* Short-circuit: if no callback and only one array, just return it. */
4454        if (!ZEND_FCI_INITIALIZED(fci)) {
4455            RETVAL_ZVAL(&arrays[0], 1, 0);
4456            return;
4457        }
4458
4459        array_init_size(return_value, maxlen);
4460
4461        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
4462            fci.retval = &result;
4463            fci.param_count = 1;
4464            fci.params = &arg;
4465            fci.no_separation = 0;
4466
4467            ZVAL_COPY(&arg, zv);
4468
4469            if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
4470                php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
4471                zval_dtor(return_value);
4472                zval_ptr_dtor(&arg);
4473                RETURN_NULL();
4474            } else {
4475                zval_ptr_dtor(&arg);
4476            }
4477            if (str_key) {
4478                zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result);
4479            } else {
4480                zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
4481            }
4482        } ZEND_HASH_FOREACH_END();
4483    } else {
4484        uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
4485
4486        for (i = 0; i < n_arrays; i++) {
4487            if (Z_TYPE(arrays[i]) != IS_ARRAY) {
4488                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2);
4489                efree(array_pos);
4490                return;
4491            }
4492            if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
4493                maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
4494            }
4495        }
4496
4497        array_init_size(return_value, maxlen);
4498
4499        if (!ZEND_FCI_INITIALIZED(fci)) {
4500            zval zv;
4501
4502            /* We iterate through all the arrays at once. */
4503            for (k = 0; k < maxlen; k++) {
4504
4505                /* If no callback, the result will be an array, consisting of current
4506                 * entries from all arrays. */
4507                array_init_size(&result, n_arrays);
4508
4509                for (i = 0; i < n_arrays; i++) {
4510                    /* If this array still has elements, add the current one to the
4511                     * parameter list, otherwise use null value. */
4512                    uint32_t pos = array_pos[i];
4513                    while (1) {
4514                        if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
4515                            ZVAL_NULL(&zv);
4516                            break;
4517                        } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
4518                            ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
4519                            array_pos[i] = pos + 1;
4520                            break;
4521                        }
4522                        pos++;
4523                    }
4524
4525                    zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
4526                }
4527
4528                zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
4529            }
4530        } else {
4531            zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
4532
4533            /* We iterate through all the arrays at once. */
4534            for (k = 0; k < maxlen; k++) {
4535                for (i = 0; i < n_arrays; i++) {
4536                    /* If this array still has elements, add the current one to the
4537                     * parameter list, otherwise use null value. */
4538                    uint32_t pos = array_pos[i];
4539                    while (1) {
4540                        if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
4541                            ZVAL_NULL(&params[i]);
4542                            break;
4543                        } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
4544                            ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
4545                            array_pos[i] = pos + 1;
4546                            break;
4547                        }
4548                        pos++;
4549                    }
4550                }
4551
4552                fci.retval = &result;
4553                fci.param_count = n_arrays;
4554                fci.params = params;
4555                fci.no_separation = 0;
4556
4557                if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
4558                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
4559                    efree(array_pos);
4560                    zval_dtor(return_value);
4561                    for (i = 0; i < n_arrays; i++) {
4562                        zval_ptr_dtor(&params[i]);
4563                    }
4564                    efree(params);
4565                    RETURN_NULL();
4566                } else {
4567                    for (i = 0; i < n_arrays; i++) {
4568                        zval_ptr_dtor(&params[i]);
4569                    }
4570                }
4571
4572                zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
4573            }
4574
4575            efree(params);
4576        }
4577        efree(array_pos);
4578    }
4579}
4580/* }}} */
4581
4582/* {{{ proto bool array_key_exists(mixed key, array search)
4583   Checks if the given key or index exists in the array */
4584PHP_FUNCTION(array_key_exists)
4585{
4586    zval *key;                  /* key to check for */
4587    HashTable *array;           /* array to check in */
4588
4589#ifndef FAST_ZPP
4590    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zH", &key, &array) == FAILURE) {
4591        return;
4592    }
4593#else
4594    ZEND_PARSE_PARAMETERS_START(2, 2)
4595        Z_PARAM_ZVAL(key)
4596        Z_PARAM_ARRAY_OR_OBJECT_HT(array)
4597    ZEND_PARSE_PARAMETERS_END();
4598#endif
4599
4600    switch (Z_TYPE_P(key)) {
4601        case IS_STRING:
4602            if (zend_symtable_exists(array, Z_STR_P(key))) {
4603                RETURN_TRUE;
4604            }
4605            RETURN_FALSE;
4606        case IS_LONG:
4607            if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
4608                RETURN_TRUE;
4609            }
4610            RETURN_FALSE;
4611        case IS_NULL:
4612            if (zend_hash_exists(array, STR_EMPTY_ALLOC())) {
4613                RETURN_TRUE;
4614            }
4615            RETURN_FALSE;
4616
4617        default:
4618            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
4619            RETURN_FALSE;
4620    }
4621}
4622/* }}} */
4623
4624/* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
4625   Split array into chunks */
4626PHP_FUNCTION(array_chunk)
4627{
4628    int argc = ZEND_NUM_ARGS(), num_in;
4629    zend_long size, current = 0;
4630    zend_string *str_key;
4631    zend_ulong num_key;
4632    zend_bool preserve_keys = 0;
4633    zval *input = NULL;
4634    zval chunk;
4635    zval *entry;
4636
4637    if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) {
4638        return;
4639    }
4640    /* Do bounds checking for size parameter. */
4641    if (size < 1) {
4642        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0");
4643        return;
4644    }
4645
4646    num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
4647
4648    if (size > num_in) {
4649        size = num_in > 0 ? num_in : 1;
4650    }
4651
4652    array_init_size(return_value, ((num_in - 1) / size) + 1);
4653
4654    ZVAL_UNDEF(&chunk);
4655
4656    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
4657        /* If new chunk, create and initialize it. */
4658        if (Z_TYPE(chunk) == IS_UNDEF) {
4659            array_init_size(&chunk, size);
4660        }
4661
4662        /* Add entry to the chunk, preserving keys if necessary. */
4663        zval_add_ref(entry);
4664
4665        if (preserve_keys) {
4666            if (str_key) {
4667                zend_hash_update(Z_ARRVAL(chunk), str_key, entry);
4668            } else {
4669                add_index_zval(&chunk, num_key, entry);
4670            }
4671        } else {
4672            add_next_index_zval(&chunk, entry);
4673        }
4674
4675        /* If reached the chunk size, add it to the result array, and reset the
4676         * pointer. */
4677        if (!(++current % size)) {
4678            add_next_index_zval(return_value, &chunk);
4679            ZVAL_UNDEF(&chunk);
4680        }
4681    } ZEND_HASH_FOREACH_END();
4682
4683    /* Add the final chunk if there is one. */
4684    if (Z_TYPE(chunk) != IS_UNDEF) {
4685        add_next_index_zval(return_value, &chunk);
4686    }
4687}
4688/* }}} */
4689
4690/* {{{ proto array array_combine(array keys, array values)
4691   Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
4692PHP_FUNCTION(array_combine)
4693{
4694    zval *values, *keys;
4695    uint32_t pos_values = 0;
4696    zval *entry_keys, *entry_values;
4697    int num_keys, num_values;
4698
4699    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) {
4700        return;
4701    }
4702
4703    num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys));
4704    num_values = zend_hash_num_elements(Z_ARRVAL_P(values));
4705
4706    if (num_keys != num_values) {
4707        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements");
4708        RETURN_FALSE;
4709    }
4710
4711    array_init_size(return_value, num_keys);
4712
4713    if (!num_keys) {
4714        return;
4715    }
4716
4717    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry_keys) {
4718        while (1) {
4719            if (pos_values >= Z_ARRVAL_P(values)->nNumUsed) {
4720                break;
4721            } else if (Z_TYPE(Z_ARRVAL_P(values)->arData[pos_values].val) != IS_UNDEF) {
4722                entry_values = &Z_ARRVAL_P(values)->arData[pos_values].val;
4723                if (Z_TYPE_P(entry_keys) == IS_LONG) {
4724                    zval_add_ref(entry_values);
4725                    add_index_zval(return_value, Z_LVAL_P(entry_keys), entry_values);
4726                } else {
4727                    zend_string *key = zval_get_string(entry_keys);
4728
4729                    zval_add_ref(entry_values);
4730                    zend_symtable_update(Z_ARRVAL_P(return_value), key, entry_values);
4731                    zend_string_release(key);
4732                }
4733                pos_values++;
4734                break;
4735            }
4736            pos_values++;
4737        }
4738    } ZEND_HASH_FOREACH_END();
4739}
4740/* }}} */
4741
4742/*
4743 * Local variables:
4744 * tab-width: 4
4745 * c-basic-offset: 4
4746 * End:
4747 * vim600: noet sw=4 ts=4 fdm=marker
4748 * vim<600: noet sw=4 ts=4
4749 */
4750