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