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