Back to index

php5  5.3.10
array.c
Go to the documentation of this file.
00001 /* 
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Andi Gutmans <andi@zend.com>                                |
00016    |          Zeev Suraski <zeev@zend.com>                                |
00017    |          Rasmus Lerdorf <rasmus@php.net>                             |
00018    |          Andrei Zmievski <andrei@php.net>                            |
00019    |          Stig Venaas <venaas@php.net>                                |
00020    |          Jason Greene <jason@php.net>                                |
00021    +----------------------------------------------------------------------+
00022 */
00023 
00024 /* $Id: array.c 321634 2012-01-01 13:15:04Z felipe $ */
00025 
00026 #include "php.h"
00027 #include "php_ini.h"
00028 #include <stdarg.h>
00029 #include <stdlib.h>
00030 #include <math.h>
00031 #include <time.h>
00032 #include <stdio.h>
00033 #if HAVE_STRING_H
00034 #include <string.h>
00035 #else
00036 #include <strings.h>
00037 #endif
00038 #ifdef PHP_WIN32
00039 #include "win32/unistd.h"
00040 #endif
00041 #include "zend_globals.h"
00042 #include "zend_interfaces.h"
00043 #include "php_globals.h"
00044 #include "php_array.h"
00045 #include "basic_functions.h"
00046 #include "php_string.h"
00047 #include "php_rand.h"
00048 #include "php_smart_str.h"
00049 #ifdef HAVE_SPL
00050 #include "ext/spl/spl_array.h"
00051 #endif
00052 
00053 /* {{{ defines */
00054 #define EXTR_OVERWRITE                    0
00055 #define EXTR_SKIP                         1
00056 #define EXTR_PREFIX_SAME           2
00057 #define       EXTR_PREFIX_ALL                    3
00058 #define       EXTR_PREFIX_INVALID         4
00059 #define       EXTR_PREFIX_IF_EXISTS       5
00060 #define       EXTR_IF_EXISTS                     6
00061 
00062 #define EXTR_REFS                         0x100
00063 
00064 #define CASE_LOWER                        0
00065 #define CASE_UPPER                        1
00066 
00067 #define COUNT_NORMAL               0
00068 #define COUNT_RECURSIVE                   1
00069 
00070 #define DIFF_NORMAL                1
00071 #define DIFF_KEY                   2
00072 #define DIFF_ASSOC                 6
00073 #define DIFF_COMP_DATA_NONE    -1
00074 #define DIFF_COMP_DATA_INTERNAL 0
00075 #define DIFF_COMP_DATA_USER     1
00076 #define DIFF_COMP_KEY_INTERNAL  0
00077 #define DIFF_COMP_KEY_USER      1
00078 
00079 #define INTERSECT_NORMAL           1
00080 #define INTERSECT_KEY                     2
00081 #define INTERSECT_ASSOC                   6
00082 #define INTERSECT_COMP_DATA_NONE    -1
00083 #define INTERSECT_COMP_DATA_INTERNAL 0
00084 #define INTERSECT_COMP_DATA_USER     1
00085 #define INTERSECT_COMP_KEY_INTERNAL  0
00086 #define INTERSECT_COMP_KEY_USER      1
00087 
00088 #define DOUBLE_DRIFT_FIX    0.000000000000001
00089 /* }}} */
00090 
00091 ZEND_DECLARE_MODULE_GLOBALS(array)
00092 
00093 /* {{{ php_array_init_globals
00094 */
00095 static void php_array_init_globals(zend_array_globals *array_globals)
00096 {
00097        memset(array_globals, 0, sizeof(zend_array_globals));
00098 }
00099 /* }}} */
00100 
00101 PHP_MINIT_FUNCTION(array) /* {{{ */
00102 {
00103        ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
00104 
00105        REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
00106        REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
00107        REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
00108        REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
00109        REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
00110        REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
00111        REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
00112        REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
00113 
00114        REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
00115        REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
00116 
00117        REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
00118        REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
00119        REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
00120        REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
00121 
00122        REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
00123        REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
00124 
00125        REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
00126        REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
00127 
00128        return SUCCESS;
00129 }
00130 /* }}} */
00131 
00132 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
00133 {
00134 #ifdef ZTS
00135        ts_free_id(array_globals_id);
00136 #endif
00137 
00138        return SUCCESS;
00139 }
00140 /* }}} */
00141 
00142 static void php_set_compare_func(int sort_type TSRMLS_DC) /* {{{ */
00143 {
00144        switch (sort_type) {
00145               case PHP_SORT_NUMERIC:
00146                      ARRAYG(compare_func) = numeric_compare_function;
00147                      break;
00148 
00149               case PHP_SORT_STRING:
00150                      ARRAYG(compare_func) = string_compare_function;
00151                      break;
00152 
00153 #if HAVE_STRCOLL
00154               case PHP_SORT_LOCALE_STRING:
00155                      ARRAYG(compare_func) = string_locale_compare_function;
00156                      break;
00157 #endif
00158 
00159               case PHP_SORT_REGULAR:
00160               default:
00161                      ARRAYG(compare_func) = compare_function;
00162                      break;
00163        }
00164 }
00165 /* }}} */
00166 
00167 static int php_array_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00168 {
00169        Bucket *f;
00170        Bucket *s;
00171        zval result;
00172        zval first;
00173        zval second;
00174 
00175        f = *((Bucket **) a);
00176        s = *((Bucket **) b);
00177 
00178        if (f->nKeyLength == 0) {
00179               Z_TYPE(first) = IS_LONG;
00180               Z_LVAL(first) = f->h;
00181        } else {
00182               Z_TYPE(first) = IS_STRING;
00183               Z_STRVAL(first) = f->arKey;
00184               Z_STRLEN(first) = f->nKeyLength - 1;
00185        }
00186 
00187        if (s->nKeyLength == 0) {
00188               Z_TYPE(second) = IS_LONG;
00189               Z_LVAL(second) = s->h;
00190        } else {
00191               Z_TYPE(second) = IS_STRING;
00192               Z_STRVAL(second) = s->arKey;
00193               Z_STRLEN(second) = s->nKeyLength - 1;
00194        }
00195 
00196        if (ARRAYG(compare_func)(&result, &first, &second TSRMLS_CC) == FAILURE) {
00197               return 0;
00198        }
00199 
00200        if (Z_TYPE(result) == IS_DOUBLE) {
00201               if (Z_DVAL(result) < 0) {
00202                      return -1;
00203               } else if (Z_DVAL(result) > 0) {
00204                      return 1;
00205               } else {
00206                      return 0;
00207               }
00208        }
00209 
00210        convert_to_long(&result);
00211 
00212        if (Z_LVAL(result) < 0) {
00213               return -1;
00214        } else if (Z_LVAL(result) > 0) {
00215               return 1;
00216        }
00217 
00218        return 0;
00219 }
00220 /* }}} */
00221 
00222 static int php_array_reverse_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00223 {
00224        return php_array_key_compare(a, b TSRMLS_CC) * -1;
00225 }
00226 /* }}} */
00227 
00228 /* {{{ proto bool krsort(array &array_arg [, int sort_flags])
00229    Sort an array by key value in reverse order */
00230 PHP_FUNCTION(krsort)
00231 {
00232        zval *array;
00233        long sort_type = PHP_SORT_REGULAR;
00234 
00235        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00236               RETURN_FALSE;
00237        }
00238 
00239        php_set_compare_func(sort_type TSRMLS_CC);
00240 
00241        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_key_compare, 0 TSRMLS_CC) == FAILURE) {
00242               RETURN_FALSE;
00243        }
00244        RETURN_TRUE;
00245 }
00246 /* }}} */
00247 
00248 /* {{{ proto bool ksort(array &array_arg [, int sort_flags])
00249    Sort an array by key */
00250 PHP_FUNCTION(ksort)
00251 {
00252        zval *array;
00253        long sort_type = PHP_SORT_REGULAR;
00254 
00255        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00256               RETURN_FALSE;
00257        }
00258 
00259        php_set_compare_func(sort_type TSRMLS_CC);
00260 
00261        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_key_compare, 0 TSRMLS_CC) == FAILURE) {
00262               RETURN_FALSE;
00263        }
00264        RETURN_TRUE;
00265 }
00266 /* }}} */
00267 
00268 static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
00269 {
00270        long cnt = 0;
00271        zval **element;
00272 
00273        if (Z_TYPE_P(array) == IS_ARRAY) {
00274               if (Z_ARRVAL_P(array)->nApplyCount > 1) {
00275                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
00276                      return 0;
00277               }
00278 
00279               cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
00280               if (mode == COUNT_RECURSIVE) {
00281                      HashPosition pos;
00282 
00283                      for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
00284                             zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
00285                             zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
00286                      ) {
00287                             Z_ARRVAL_P(array)->nApplyCount++;
00288                             cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
00289                             Z_ARRVAL_P(array)->nApplyCount--;
00290                      }
00291               }
00292        }
00293 
00294        return cnt;
00295 }
00296 /* }}} */
00297 
00298 /* {{{ proto int count(mixed var [, int mode])
00299    Count the number of elements in a variable (usually an array) */
00300 PHP_FUNCTION(count)
00301 {
00302        zval *array;
00303        long mode = COUNT_NORMAL;
00304 
00305        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
00306               return;
00307        }
00308 
00309        switch (Z_TYPE_P(array)) {
00310               case IS_NULL:
00311                      RETURN_LONG(0);
00312                      break;
00313               case IS_ARRAY:
00314                      RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
00315                      break;
00316               case IS_OBJECT: {
00317 #ifdef HAVE_SPL
00318                      zval *retval;
00319 #endif
00320                      /* first, we check if the handler is defined */
00321                      if (Z_OBJ_HT_P(array)->count_elements) {
00322                             RETVAL_LONG(1);
00323                             if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value) TSRMLS_CC)) {
00324                                    return;
00325                             }
00326                      }
00327 #ifdef HAVE_SPL
00328                      /* if not and the object implements Countable we call its count() method */
00329                      if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
00330                             zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval);
00331                             if (retval) {
00332                                    convert_to_long_ex(&retval);
00333                                    RETVAL_LONG(Z_LVAL_P(retval));
00334                                    zval_ptr_dtor(&retval);
00335                             }
00336                             return;
00337                      }
00338 #endif
00339               }
00340               default:
00341                      RETURN_LONG(1);
00342                      break;
00343        }
00344 }
00345 /* }}} */
00346 
00347 /* Numbers are always smaller than strings int this function as it
00348  * anyway doesn't make much sense to compare two different data types.
00349  * This keeps it consistant and simple.
00350  *
00351  * This is not correct any more, depends on what compare_func is set to.
00352  */
00353 static int php_array_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00354 {
00355        Bucket *f;
00356        Bucket *s;
00357        zval result;
00358        zval *first;
00359        zval *second;
00360 
00361        f = *((Bucket **) a);
00362        s = *((Bucket **) b);
00363 
00364        first = *((zval **) f->pData);
00365        second = *((zval **) s->pData);
00366 
00367        if (ARRAYG(compare_func)(&result, first, second TSRMLS_CC) == FAILURE) {
00368               return 0;
00369        }
00370 
00371        if (Z_TYPE(result) == IS_DOUBLE) {
00372               if (Z_DVAL(result) < 0) {
00373                      return -1;
00374               } else if (Z_DVAL(result) > 0) {
00375                      return 1;
00376               } else {
00377                      return 0;
00378               }
00379        }
00380 
00381        convert_to_long(&result);
00382 
00383        if (Z_LVAL(result) < 0) {
00384               return -1;
00385        } else if (Z_LVAL(result) > 0) {
00386               return 1;
00387        }
00388 
00389        return 0;
00390 }
00391 /* }}} */
00392 
00393 static int php_array_reverse_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00394 {
00395        return php_array_data_compare(a, b TSRMLS_CC) * -1;
00396 }
00397 /* }}} */
00398 
00399 static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */
00400 {
00401        Bucket *f, *s;
00402        zval *fval, *sval;
00403        zval first, second;
00404        int result;
00405 
00406        f = *((Bucket **) a);
00407        s = *((Bucket **) b);
00408 
00409        fval = *((zval **) f->pData);
00410        sval = *((zval **) s->pData);
00411        first = *fval;
00412        second = *sval;
00413 
00414        if (Z_TYPE_P(fval) != IS_STRING) {
00415               zval_copy_ctor(&first);
00416               convert_to_string(&first);
00417        }
00418 
00419        if (Z_TYPE_P(sval) != IS_STRING) {
00420               zval_copy_ctor(&second);
00421               convert_to_string(&second);
00422        }
00423 
00424        result = strnatcmp_ex(Z_STRVAL(first), Z_STRLEN(first), Z_STRVAL(second), Z_STRLEN(second), fold_case);
00425 
00426        if (Z_TYPE_P(fval) != IS_STRING) {
00427               zval_dtor(&first);
00428        }
00429 
00430        if (Z_TYPE_P(sval) != IS_STRING) {
00431               zval_dtor(&second);
00432        }
00433 
00434        return result;
00435 }
00436 /* }}} */
00437 
00438 static int php_array_natural_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00439 {
00440        return php_array_natural_general_compare(a, b, 0);
00441 }
00442 /* }}} */
00443 
00444 static int php_array_natural_case_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00445 {
00446        return php_array_natural_general_compare(a, b, 1);
00447 }
00448 /* }}} */
00449 
00450 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
00451 {
00452        zval *array;
00453 
00454        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
00455               return;
00456        }
00457 
00458        if (fold_case) {
00459               if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_case_compare, 0 TSRMLS_CC) == FAILURE) {
00460                      return;
00461               }
00462        } else {
00463               if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_compare, 0 TSRMLS_CC) == FAILURE) {
00464                      return;
00465               }
00466        }
00467 
00468        RETURN_TRUE;
00469 }
00470 /* }}} */
00471 
00472 /* {{{ proto void natsort(array &array_arg)
00473    Sort an array using natural sort */
00474 PHP_FUNCTION(natsort)
00475 {
00476        php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
00477 }
00478 /* }}} */
00479 
00480 /* {{{ proto void natcasesort(array &array_arg)
00481    Sort an array using case-insensitive natural sort */
00482 PHP_FUNCTION(natcasesort)
00483 {
00484        php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
00485 }
00486 /* }}} */
00487 
00488 /* {{{ proto bool asort(array &array_arg [, int sort_flags])
00489    Sort an array and maintain index association */
00490 PHP_FUNCTION(asort)
00491 {
00492        zval *array;
00493        long sort_type = PHP_SORT_REGULAR;
00494 
00495        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00496               RETURN_FALSE;
00497        }
00498 
00499        php_set_compare_func(sort_type TSRMLS_CC);
00500 
00501        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 0 TSRMLS_CC) == FAILURE) {
00502               RETURN_FALSE;
00503        }
00504        RETURN_TRUE;
00505 }
00506 /* }}} */
00507 
00508 /* {{{ proto bool arsort(array &array_arg [, int sort_flags])
00509    Sort an array in reverse order and maintain index association */
00510 PHP_FUNCTION(arsort)
00511 {
00512        zval *array;
00513        long sort_type = PHP_SORT_REGULAR;
00514 
00515        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00516               RETURN_FALSE;
00517        }
00518 
00519        php_set_compare_func(sort_type TSRMLS_CC);
00520 
00521        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 0 TSRMLS_CC) == FAILURE) {
00522               RETURN_FALSE;
00523        }
00524        RETURN_TRUE;
00525 }
00526 /* }}} */
00527 
00528 /* {{{ proto bool sort(array &array_arg [, int sort_flags])
00529    Sort an array */
00530 PHP_FUNCTION(sort)
00531 {
00532        zval *array;
00533        long sort_type = PHP_SORT_REGULAR;
00534 
00535        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00536               RETURN_FALSE;
00537        }
00538 
00539        php_set_compare_func(sort_type TSRMLS_CC);
00540 
00541        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 1 TSRMLS_CC) == FAILURE) {
00542               RETURN_FALSE;
00543        }
00544        RETURN_TRUE;
00545 }
00546 /* }}} */
00547 
00548 /* {{{ proto bool rsort(array &array_arg [, int sort_flags])
00549    Sort an array in reverse order */
00550 PHP_FUNCTION(rsort)
00551 {
00552        zval *array;
00553        long sort_type = PHP_SORT_REGULAR;
00554 
00555        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
00556               RETURN_FALSE;
00557        }
00558 
00559        php_set_compare_func(sort_type TSRMLS_CC);
00560 
00561        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 1 TSRMLS_CC) == FAILURE) {
00562               RETURN_FALSE;
00563        }
00564        RETURN_TRUE;
00565 }
00566 /* }}} */
00567 
00568 static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00569 {
00570        Bucket *f;
00571        Bucket *s;
00572        zval **args[2];
00573        zval *retval_ptr = NULL;
00574 
00575        f = *((Bucket **) a);
00576        s = *((Bucket **) b);
00577 
00578        args[0] = (zval **) f->pData;
00579        args[1] = (zval **) s->pData;
00580 
00581        BG(user_compare_fci).param_count = 2;
00582        BG(user_compare_fci).params = args;
00583        BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
00584        BG(user_compare_fci).no_separation = 0;
00585        if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
00586               long retval;
00587 
00588               convert_to_long_ex(&retval_ptr);
00589               retval = Z_LVAL_P(retval_ptr);
00590               zval_ptr_dtor(&retval_ptr);
00591               return retval < 0 ? -1 : retval > 0 ? 1 : 0;
00592        } else {
00593               return 0;
00594        }
00595 }
00596 /* }}} */
00597 
00598 /* check if comparison function is valid */
00599 #define PHP_ARRAY_CMP_FUNC_CHECK(func_name)      \
00600        if (!zend_is_callable(*func_name, 0, NULL TSRMLS_CC)) { \
00601               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid comparison function");  \
00602               BG(user_compare_fci) = old_user_compare_fci; \
00603               BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
00604               RETURN_FALSE; \
00605        }      \
00606 
00607        /* Clear FCI cache otherwise : for example the same or other array with
00608         * (partly) the same key values has been sorted with uasort() or
00609         * other sorting function the comparison is cached, however the name
00610         * of the function for comparison is not respected. see bug #28739 AND #33295
00611         *
00612         * Following defines will assist in backup / restore values. */
00613 
00614 #define PHP_ARRAY_CMP_FUNC_VARS \
00615        zend_fcall_info old_user_compare_fci; \
00616        zend_fcall_info_cache old_user_compare_fci_cache \
00617 
00618 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
00619        old_user_compare_fci = BG(user_compare_fci); \
00620        old_user_compare_fci_cache = BG(user_compare_fci_cache); \
00621        BG(user_compare_fci_cache) = empty_fcall_info_cache; \
00622 
00623 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
00624        BG(user_compare_fci) = old_user_compare_fci; \
00625        BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
00626 
00627 /* {{{ proto bool usort(array array_arg, string cmp_function)
00628    Sort an array by values using a user-defined comparison function */
00629 PHP_FUNCTION(usort)
00630 {
00631        zval *array;
00632        int refcount;
00633        PHP_ARRAY_CMP_FUNC_VARS;
00634 
00635        PHP_ARRAY_CMP_FUNC_BACKUP();
00636 
00637        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
00638               PHP_ARRAY_CMP_FUNC_RESTORE();
00639               return;
00640        }
00641 
00642        /* Clear the is_ref flag, so the attemts to modify the array in user
00643         * comparison function will create a copy of array and won't affect the
00644         * original array. The fact of modification is detected using refcount
00645         * comparison. The result of sorting in such case is undefined and the
00646         * function returns FALSE.
00647         */
00648        Z_UNSET_ISREF_P(array);
00649        refcount = Z_REFCOUNT_P(array);
00650 
00651        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 1 TSRMLS_CC) == FAILURE) {
00652               RETVAL_FALSE;
00653        } else {
00654               if (refcount > Z_REFCOUNT_P(array)) {
00655                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
00656                      RETVAL_FALSE;
00657               } else {
00658                      RETVAL_TRUE;
00659               }
00660        }
00661        
00662        if (Z_REFCOUNT_P(array) > 1) {
00663               Z_SET_ISREF_P(array);
00664        }
00665 
00666        PHP_ARRAY_CMP_FUNC_RESTORE();
00667 }
00668 /* }}} */
00669 
00670 /* {{{ proto bool uasort(array array_arg, string cmp_function)
00671    Sort an array with a user-defined comparison function and maintain index association */
00672 PHP_FUNCTION(uasort)
00673 {
00674        zval *array;
00675        int refcount;
00676        PHP_ARRAY_CMP_FUNC_VARS;
00677 
00678        PHP_ARRAY_CMP_FUNC_BACKUP();
00679 
00680        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
00681               PHP_ARRAY_CMP_FUNC_RESTORE();
00682               return;
00683        }
00684 
00685        /* Clear the is_ref flag, so the attemts to modify the array in user
00686         * comaprison function will create a copy of array and won't affect the
00687         * original array. The fact of modification is detected using refcount
00688         * comparison. The result of sorting in such case is undefined and the
00689         * function returns FALSE.
00690         */
00691        Z_UNSET_ISREF_P(array);
00692        refcount = Z_REFCOUNT_P(array);
00693 
00694        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 0 TSRMLS_CC) == FAILURE) {
00695               RETVAL_FALSE;
00696        } else {
00697               if (refcount > Z_REFCOUNT_P(array)) {
00698                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
00699                      RETVAL_FALSE;
00700               } else {
00701                      RETVAL_TRUE;
00702               }
00703        }
00704 
00705        if (Z_REFCOUNT_P(array) > 1) {
00706               Z_SET_ISREF_P(array);
00707        }
00708 
00709        PHP_ARRAY_CMP_FUNC_RESTORE();
00710 }
00711 /* }}} */
00712 
00713 static int php_array_user_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
00714 {
00715        Bucket *f;
00716        Bucket *s;
00717        zval *key1, *key2;
00718        zval **args[2];
00719        zval *retval_ptr = NULL;
00720        long result;
00721 
00722        ALLOC_INIT_ZVAL(key1);
00723        ALLOC_INIT_ZVAL(key2);
00724        args[0] = &key1;
00725        args[1] = &key2;
00726 
00727        f = *((Bucket **) a);
00728        s = *((Bucket **) b);
00729 
00730        if (f->nKeyLength == 0) {
00731               Z_LVAL_P(key1) = f->h;
00732               Z_TYPE_P(key1) = IS_LONG;
00733        } else {
00734               Z_STRVAL_P(key1) = estrndup(f->arKey, f->nKeyLength - 1);
00735               Z_STRLEN_P(key1) = f->nKeyLength - 1;
00736               Z_TYPE_P(key1) = IS_STRING;
00737        }
00738        if (s->nKeyLength == 0) {
00739               Z_LVAL_P(key2) = s->h;
00740               Z_TYPE_P(key2) = IS_LONG;
00741        } else {
00742               Z_STRVAL_P(key2) = estrndup(s->arKey, s->nKeyLength - 1);
00743               Z_STRLEN_P(key2) = s->nKeyLength - 1;
00744               Z_TYPE_P(key2) = IS_STRING;
00745        }
00746 
00747        BG(user_compare_fci).param_count = 2;
00748        BG(user_compare_fci).params = args;
00749        BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
00750        BG(user_compare_fci).no_separation = 0;
00751        if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
00752               convert_to_long_ex(&retval_ptr);
00753               result = Z_LVAL_P(retval_ptr);
00754               zval_ptr_dtor(&retval_ptr);
00755        } else {
00756               result = 0;
00757        }
00758 
00759        zval_ptr_dtor(&key1);
00760        zval_ptr_dtor(&key2);
00761 
00762        return result;
00763 }
00764 /* }}} */
00765 
00766 /* {{{ proto bool uksort(array array_arg, string cmp_function)
00767    Sort an array by keys using a user-defined comparison function */
00768 PHP_FUNCTION(uksort)
00769 {
00770        zval *array;
00771        int refcount;
00772        PHP_ARRAY_CMP_FUNC_VARS;
00773 
00774        PHP_ARRAY_CMP_FUNC_BACKUP();
00775 
00776        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
00777               PHP_ARRAY_CMP_FUNC_RESTORE();
00778               return;
00779        }
00780 
00781        /* Clear the is_ref flag, so the attemts to modify the array in user
00782         * comaprison function will create a copy of array and won't affect the
00783         * original array. The fact of modification is detected using refcount
00784         * comparison. The result of sorting in such case is undefined and the
00785         * function returns FALSE.
00786         */
00787        Z_UNSET_ISREF_P(array);
00788        refcount = Z_REFCOUNT_P(array);
00789 
00790        if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_key_compare, 0 TSRMLS_CC) == FAILURE) {
00791               RETVAL_FALSE;
00792        } else {
00793               if (refcount > Z_REFCOUNT_P(array)) {
00794                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
00795                      RETVAL_FALSE;
00796               } else {
00797                      RETVAL_TRUE;
00798               }
00799        }
00800 
00801        if (Z_REFCOUNT_P(array) > 1) {
00802               Z_SET_ISREF_P(array);
00803        }
00804 
00805        PHP_ARRAY_CMP_FUNC_RESTORE();
00806 }
00807 /* }}} */
00808 
00809 /* {{{ proto mixed end(array array_arg)
00810    Advances array argument's internal pointer to the last element and return it */
00811 PHP_FUNCTION(end)
00812 {
00813        HashTable *array;
00814        zval **entry;
00815 
00816        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00817               return;
00818        }
00819 
00820        zend_hash_internal_pointer_end(array);
00821 
00822        if (return_value_used) {
00823               if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
00824                      RETURN_FALSE;
00825               }
00826 
00827               RETURN_ZVAL(*entry, 1, 0);
00828        }
00829 }
00830 /* }}} */
00831 
00832 /* {{{ proto mixed prev(array array_arg)
00833    Move array argument's internal pointer to the previous element and return it */
00834 PHP_FUNCTION(prev)
00835 {
00836        HashTable *array;
00837        zval **entry;
00838 
00839        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00840               return;
00841        }
00842 
00843        zend_hash_move_backwards(array);
00844 
00845        if (return_value_used) {
00846               if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
00847                      RETURN_FALSE;
00848               }
00849 
00850               RETURN_ZVAL(*entry, 1, 0);
00851        }
00852 }
00853 /* }}} */
00854 
00855 /* {{{ proto mixed next(array array_arg)
00856    Move array argument's internal pointer to the next element and return it */
00857 PHP_FUNCTION(next)
00858 {
00859        HashTable *array;
00860        zval **entry;
00861 
00862        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00863               return;
00864        }
00865 
00866        zend_hash_move_forward(array);
00867 
00868        if (return_value_used) {
00869               if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
00870                      RETURN_FALSE;
00871               }
00872 
00873               RETURN_ZVAL(*entry, 1, 0);
00874        }
00875 }
00876 /* }}} */
00877 
00878 /* {{{ proto mixed reset(array array_arg)
00879    Set array argument's internal pointer to the first element and return it */
00880 PHP_FUNCTION(reset)
00881 {
00882        HashTable *array;
00883        zval **entry;
00884 
00885        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00886               return;
00887        }
00888 
00889        zend_hash_internal_pointer_reset(array);
00890 
00891        if (return_value_used) {
00892               if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
00893                      RETURN_FALSE;
00894               }
00895 
00896               RETURN_ZVAL(*entry, 1, 0);
00897        }
00898 }
00899 /* }}} */
00900 
00901 /* {{{ proto mixed current(array array_arg)
00902    Return the element currently pointed to by the internal array pointer */
00903 PHP_FUNCTION(current)
00904 {
00905        HashTable *array;
00906        zval **entry;
00907 
00908        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00909               return;
00910        }
00911 
00912        if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
00913               RETURN_FALSE;
00914        }
00915        RETURN_ZVAL(*entry, 1, 0);
00916 }
00917 /* }}} */
00918 
00919 /* {{{ proto mixed key(array array_arg)
00920    Return the key of the element currently pointed to by the internal array pointer */
00921 PHP_FUNCTION(key)
00922 {
00923        HashTable *array;
00924        char *string_key;
00925        uint string_length;
00926        ulong num_key;
00927 
00928        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
00929               return;
00930        }
00931 
00932        switch (zend_hash_get_current_key_ex(array, &string_key, &string_length, &num_key, 0, NULL)) {
00933               case HASH_KEY_IS_STRING:
00934                      RETVAL_STRINGL(string_key, string_length - 1, 1);
00935                      break;
00936               case HASH_KEY_IS_LONG:
00937                      RETVAL_LONG(num_key);
00938                      break;
00939               case HASH_KEY_NON_EXISTANT:
00940                      return;
00941        }
00942 }
00943 /* }}} */
00944 
00945 /* {{{ proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
00946    Return the lowest value in an array or a series of arguments */
00947 PHP_FUNCTION(min)
00948 {
00949        int argc;
00950        zval ***args = NULL;
00951 
00952        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
00953               return;
00954        }
00955        
00956        php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
00957        
00958        /* mixed min ( array $values ) */
00959        if (argc == 1) {
00960               zval **result;
00961               
00962               if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
00963                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
00964                      RETVAL_NULL();
00965               } else {
00966                      if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) {
00967                             RETVAL_ZVAL(*result, 1, 0);
00968                      } else {
00969                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
00970                             RETVAL_FALSE;
00971                      }
00972               }
00973        } else {
00974               /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
00975               zval **min, result;
00976               int i;
00977 
00978               min = args[0];
00979 
00980               for (i = 1; i < argc; i++) {
00981                      is_smaller_function(&result, *args[i], *min TSRMLS_CC);
00982                      if (Z_LVAL(result) == 1) {
00983                             min = args[i];
00984                      }
00985               }
00986 
00987               RETVAL_ZVAL(*min, 1, 0);    
00988        }
00989 
00990        if (args) {
00991               efree(args);
00992        }
00993 }
00994 /* }}} */
00995 
00996 /* {{{ proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
00997    Return the highest value in an array or a series of arguments */
00998 PHP_FUNCTION(max)
00999 {
01000        zval ***args = NULL;
01001        int argc;
01002        
01003        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
01004               return;
01005        }
01006 
01007        php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
01008        
01009        /* mixed max ( array $values ) */
01010        if (argc == 1) {
01011               zval **result;
01012 
01013               if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
01014                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
01015                      RETVAL_NULL();
01016               } else {
01017                      if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) {
01018                             RETVAL_ZVAL(*result, 1, 0);
01019                      } else {
01020                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
01021                             RETVAL_FALSE;
01022                      }
01023               }
01024        } else {
01025               /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
01026               zval **max, result;
01027               int i;
01028 
01029               max = args[0];
01030 
01031               for (i = 1; i < argc; i++) {
01032                      is_smaller_or_equal_function(&result, *args[i], *max TSRMLS_CC);
01033                      if (Z_LVAL(result) == 0) {
01034                             max = args[i];
01035                      }
01036               }
01037 
01038               RETVAL_ZVAL(*max, 1, 0);
01039        }
01040        
01041        if (args) {
01042               efree(args);
01043        }
01044 }
01045 /* }}} */
01046 
01047 static int php_array_walk(HashTable *target_hash, zval **userdata, int recursive TSRMLS_DC) /* {{{ */
01048 {
01049        zval **args[3],                    /* Arguments to userland function */
01050                 *retval_ptr,              /* Return value - unused */
01051                 *key=NULL;         /* Entry key */
01052        char  *string_key;
01053        uint   string_key_len;
01054        ulong  num_key;
01055        HashPosition pos;
01056 
01057        /* Set up known arguments */
01058        args[1] = &key;
01059        args[2] = userdata;
01060        if (userdata) {
01061               Z_ADDREF_PP(userdata);
01062        }
01063 
01064        zend_hash_internal_pointer_reset_ex(target_hash, &pos);
01065 
01066        BG(array_walk_fci).retval_ptr_ptr = &retval_ptr;
01067        BG(array_walk_fci).param_count = userdata ? 3 : 2;
01068        BG(array_walk_fci).params = args;
01069        BG(array_walk_fci).no_separation = 0;
01070 
01071        /* Iterate through hash */
01072        while (!EG(exception) && zend_hash_get_current_data_ex(target_hash, (void **)&args[0], &pos) == SUCCESS) {
01073               if (recursive && Z_TYPE_PP(args[0]) == IS_ARRAY) {
01074                      HashTable *thash;
01075                      zend_fcall_info orig_array_walk_fci;
01076                      zend_fcall_info_cache orig_array_walk_fci_cache;
01077 
01078                      SEPARATE_ZVAL_IF_NOT_REF(args[0]);
01079                      thash = Z_ARRVAL_PP(args[0]);
01080                      if (thash->nApplyCount > 1) {
01081                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
01082                             if (userdata) {
01083                                    zval_ptr_dtor(userdata);
01084                             }
01085                             return 0;
01086                      }
01087 
01088                      /* backup the fcall info and cache */
01089                      orig_array_walk_fci = BG(array_walk_fci);
01090                      orig_array_walk_fci_cache = BG(array_walk_fci_cache);
01091 
01092                      thash->nApplyCount++;
01093                      php_array_walk(thash, userdata, recursive TSRMLS_CC);
01094                      thash->nApplyCount--;
01095 
01096                      /* restore the fcall info and cache */
01097                      BG(array_walk_fci) = orig_array_walk_fci;
01098                      BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
01099               } else {
01100                      /* Allocate space for key */
01101                      MAKE_STD_ZVAL(key);
01102 
01103                      /* Set up the key */
01104                      switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_key_len, &num_key, 0, &pos)) {
01105                             case HASH_KEY_IS_LONG:
01106                                    Z_TYPE_P(key) = IS_LONG;
01107                                    Z_LVAL_P(key) = num_key;
01108                                    break;
01109                             case HASH_KEY_IS_STRING:
01110                                    ZVAL_STRINGL(key, string_key, string_key_len - 1, 1);
01111                                    break;
01112                      }
01113 
01114                      /* Call the userland function */
01115                      if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache) TSRMLS_CC) == SUCCESS) {
01116                             if (retval_ptr) {
01117                                    zval_ptr_dtor(&retval_ptr);
01118                             }
01119                      } else {
01120                             if (key) {
01121                                    zval_ptr_dtor(&key);
01122                                    key = NULL;
01123                             }
01124                             break;
01125                      }
01126               }
01127 
01128               if (key) {
01129                      zval_ptr_dtor(&key);
01130                      key = NULL;
01131               }
01132               zend_hash_move_forward_ex(target_hash, &pos);
01133        }
01134 
01135        if (userdata) {
01136               zval_ptr_dtor(userdata);
01137        }
01138        return 0;
01139 }
01140 /* }}} */
01141 
01142 /* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
01143    Apply a user function to every member of an array */
01144 PHP_FUNCTION(array_walk)
01145 {
01146        HashTable *array;
01147        zval *userdata = NULL;
01148        zend_fcall_info orig_array_walk_fci;
01149        zend_fcall_info_cache orig_array_walk_fci_cache;
01150 
01151        orig_array_walk_fci = BG(array_walk_fci);
01152        orig_array_walk_fci_cache = BG(array_walk_fci_cache);
01153 
01154        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
01155               BG(array_walk_fci) = orig_array_walk_fci;
01156               BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
01157               return;
01158        }
01159 
01160        php_array_walk(array, userdata ? &userdata : NULL, 0 TSRMLS_CC);
01161        BG(array_walk_fci) = orig_array_walk_fci;
01162        BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
01163        RETURN_TRUE;
01164 }
01165 /* }}} */
01166 
01167 /* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
01168    Apply a user function recursively to every member of an array */
01169 PHP_FUNCTION(array_walk_recursive)
01170 {
01171        HashTable *array;
01172        zval *userdata = NULL;
01173        zend_fcall_info orig_array_walk_fci;
01174        zend_fcall_info_cache orig_array_walk_fci_cache;
01175 
01176        orig_array_walk_fci = BG(array_walk_fci);
01177        orig_array_walk_fci_cache = BG(array_walk_fci_cache);
01178 
01179        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
01180               BG(array_walk_fci) = orig_array_walk_fci;
01181               BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
01182               return;
01183        }
01184 
01185        php_array_walk(array, userdata ? &userdata : NULL, 1 TSRMLS_CC);
01186        BG(array_walk_fci) = orig_array_walk_fci;
01187        BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
01188        RETURN_TRUE;
01189 }
01190 /* }}} */
01191 
01192 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
01193  * 0 = return boolean
01194  * 1 = return key
01195  */
01196 static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
01197 {
01198        zval *value,                       /* value to check for */
01199                *array,                           /* array to check in */
01200                **entry,                          /* pointer to array entry */
01201                 res;                             /* comparison result */
01202        HashPosition pos;                  /* hash iterator */
01203        zend_bool strict = 0;              /* strict comparison or not */
01204        ulong num_key;
01205        uint str_key_len;
01206        char *string_key;
01207        int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
01208 
01209        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) {
01210               return;
01211        }
01212 
01213        if (strict) {
01214               is_equal_func = is_identical_function;
01215        }
01216 
01217        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
01218        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
01219               is_equal_func(&res, value, *entry TSRMLS_CC);
01220               if (Z_LVAL(res)) {
01221                      if (behavior == 0) {
01222                             RETURN_TRUE;
01223                      } else {
01224                             /* Return current key */
01225                             switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
01226                                    case HASH_KEY_IS_STRING:
01227                                           RETURN_STRINGL(string_key, str_key_len - 1, 1);
01228                                           break;
01229                                    case HASH_KEY_IS_LONG:
01230                                           RETURN_LONG(num_key);
01231                                           break;
01232                             }
01233                      }
01234               }
01235               zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
01236        }
01237 
01238        RETURN_FALSE;
01239 }
01240 /* }}} */
01241 
01242 /* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
01243    Checks if the given value exists in the array */
01244 PHP_FUNCTION(in_array)
01245 {
01246        php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
01247 }
01248 /* }}} */
01249 
01250 /* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
01251    Searches the array for a given value and returns the corresponding key if successful */
01252 PHP_FUNCTION(array_search)
01253 {
01254        php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
01255 }
01256 /* }}} */
01257 
01258 static int php_valid_var_name(char *var_name, int var_name_len) /* {{{ */
01259 {
01260        int i, ch;
01261 
01262        if (!var_name || !var_name_len) {
01263               return 0;
01264        }
01265        
01266        /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
01267        ch = (int)((unsigned char *)var_name)[0];
01268        if (var_name[0] != '_' &&
01269               (ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
01270               (ch < 97  /* a    */ || /* z    */ ch > 122) &&
01271               (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
01272        ) {
01273               return 0;
01274        }
01275 
01276        /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
01277        if (var_name_len > 1) {
01278               for (i = 1; i < var_name_len; i++) {
01279                      ch = (int)((unsigned char *)var_name)[i];
01280                      if (var_name[i] != '_' &&
01281                             (ch < 48  /* 0    */ || /* 9    */ ch > 57)  &&
01282                             (ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
01283                             (ch < 97  /* a    */ || /* z    */ ch > 122) &&
01284                             (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
01285                      ) {
01286                             return 0;
01287                      }
01288               }
01289        }
01290        return 1;
01291 }
01292 /* }}} */
01293 
01294 PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, int var_name_len, zend_bool add_underscore TSRMLS_DC) /* {{{ */
01295 {
01296        Z_STRLEN_P(result) = Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len;
01297        Z_TYPE_P(result) = IS_STRING;
01298        Z_STRVAL_P(result) = emalloc(Z_STRLEN_P(result) + 1);
01299        memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
01300 
01301        if (add_underscore) {
01302               Z_STRVAL_P(result)[Z_STRLEN_P(prefix)] = '_';
01303        }
01304 
01305        memcpy(Z_STRVAL_P(result) + Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
01306 
01307        return SUCCESS;
01308 }
01309 /* }}} */
01310 
01311 /* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
01312    Imports variables into symbol table from an array */
01313 PHP_FUNCTION(extract)
01314 {
01315        zval *var_array, *prefix = NULL;
01316        long extract_type = EXTR_OVERWRITE;
01317        zval **entry, *data;
01318        char *var_name;
01319        ulong num_key;
01320        uint var_name_len;
01321        int var_exists, key_type, count = 0;
01322        int extract_refs = 0;
01323        HashPosition pos;
01324 
01325        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) {
01326               return;
01327        }
01328 
01329        extract_refs = (extract_type & EXTR_REFS);
01330        extract_type &= 0xff;
01331 
01332        if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
01333               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid extract type");
01334               return;
01335        }
01336 
01337        if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
01338               php_error_docref(NULL TSRMLS_CC, E_WARNING, "specified extract type requires the prefix parameter");
01339               return;
01340        }
01341 
01342        if (prefix) {
01343               convert_to_string(prefix);
01344               if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) {
01345                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "prefix is not a valid identifier");
01346                      return;
01347               }
01348        }
01349 
01350        if (!EG(active_symbol_table)) {
01351               zend_rebuild_symbol_table(TSRMLS_C);
01352        }
01353 
01354        /* var_array is passed by ref for the needs of EXTR_REFS (needs to
01355         * work on the original array to create refs to its members)
01356         * simulate pass_by_value if EXTR_REFS is not used */
01357        if (!extract_refs) {
01358               SEPARATE_ARG_IF_REF(var_array);
01359        }
01360 
01361        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos);
01362        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&entry, &pos) == SUCCESS) {
01363               zval final_name;
01364 
01365               ZVAL_NULL(&final_name);
01366 
01367               key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &var_name, &var_name_len, &num_key, 0, &pos);
01368               var_exists = 0;
01369 
01370               if (key_type == HASH_KEY_IS_STRING) {
01371                      var_name_len--;
01372                      var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
01373               } else if (key_type == HASH_KEY_IS_LONG && (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID)) {
01374                      zval num;
01375 
01376                      ZVAL_LONG(&num, num_key);
01377                      convert_to_string(&num);
01378                      php_prefix_varname(&final_name, prefix, Z_STRVAL(num), Z_STRLEN(num), 1 TSRMLS_CC);
01379                      zval_dtor(&num);
01380               } else {
01381                      zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
01382                      continue;
01383               }
01384 
01385               switch (extract_type) {
01386                      case EXTR_IF_EXISTS:
01387                             if (!var_exists) break;
01388                             /* break omitted intentionally */
01389 
01390                      case EXTR_OVERWRITE:
01391                             /* GLOBALS protection */
01392                             if (var_exists && var_name_len == sizeof("GLOBALS")-1 && !strcmp(var_name, "GLOBALS")) {
01393                                    break;
01394                             }
01395                             if (var_exists && var_name_len == sizeof("this")-1  && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) {
01396                                    break;
01397                             }
01398                             ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
01399                             break;
01400 
01401                      case EXTR_PREFIX_IF_EXISTS:
01402                             if (var_exists) {
01403                                    php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
01404                             }
01405                             break;
01406 
01407                      case EXTR_PREFIX_SAME:
01408                             if (!var_exists && var_name_len != 0) {
01409                                    ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
01410                             }
01411                             /* break omitted intentionally */
01412 
01413                      case EXTR_PREFIX_ALL:
01414                             if (Z_TYPE(final_name) == IS_NULL && var_name_len != 0) {
01415                                    php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
01416                             }
01417                             break;
01418 
01419                      case EXTR_PREFIX_INVALID:
01420                             if (Z_TYPE(final_name) == IS_NULL) {
01421                                    if (!php_valid_var_name(var_name, var_name_len)) {
01422                                           php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
01423                                    } else {
01424                                           ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
01425                                    }
01426                             }
01427                             break;
01428 
01429                      default:
01430                             if (!var_exists) {
01431                                    ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
01432                             }
01433                             break;
01434               }
01435 
01436               if (Z_TYPE(final_name) != IS_NULL && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
01437                      if (extract_refs) {
01438                             zval **orig_var;
01439 
01440                             SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
01441                             zval_add_ref(entry);
01442 
01443                             if (zend_hash_find(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) &orig_var) == SUCCESS) {
01444                                    zval_ptr_dtor(orig_var);
01445                                    *orig_var = *entry;
01446                             } else {
01447                                    zend_hash_update(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) entry, sizeof(zval *), NULL);
01448                             }
01449                      } else {
01450                             MAKE_STD_ZVAL(data);
01451                             *data = **entry;
01452                             zval_copy_ctor(data);
01453 
01454                             ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
01455                      }
01456                      count++;
01457               }
01458               zval_dtor(&final_name);
01459 
01460               zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
01461        }
01462 
01463        if (!extract_refs) {
01464               zval_ptr_dtor(&var_array);
01465        }
01466 
01467        RETURN_LONG(count);
01468 }
01469 /* }}} */
01470 
01471 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry TSRMLS_DC) /* {{{ */
01472 {
01473        zval **value_ptr, *value, *data;
01474 
01475        if (Z_TYPE_P(entry) == IS_STRING) {
01476               if (zend_hash_find(eg_active_symbol_table, Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, (void **)&value_ptr) != FAILURE) {
01477                      value = *value_ptr;
01478                      ALLOC_ZVAL(data);
01479                      MAKE_COPY_ZVAL(&value, data);
01480 
01481                      zend_hash_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, &data, sizeof(zval *), NULL);
01482               }
01483        }
01484        else if (Z_TYPE_P(entry) == IS_ARRAY) {
01485               HashPosition pos;
01486 
01487               if ((Z_ARRVAL_P(entry)->nApplyCount > 1)) {
01488                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
01489                      return;
01490               }
01491 
01492               Z_ARRVAL_P(entry)->nApplyCount++;
01493 
01494               zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
01495               while (zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), (void**)&value_ptr, &pos) == SUCCESS) {
01496                      value = *value_ptr;
01497 
01498                      php_compact_var(eg_active_symbol_table, return_value, value TSRMLS_CC);
01499                      zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos);
01500               }
01501               Z_ARRVAL_P(entry)->nApplyCount--;
01502        }
01503 }
01504 /* }}} */
01505 
01506 /* {{{ proto array compact(mixed var_names [, mixed ...])
01507    Creates a hash containing variables and their values */
01508 PHP_FUNCTION(compact)
01509 {
01510        zval ***args = NULL; /* function arguments array */
01511        int num_args, i;
01512 
01513        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
01514               return;
01515        }
01516 
01517        if (!EG(active_symbol_table)) {
01518               zend_rebuild_symbol_table(TSRMLS_C);
01519        }
01520 
01521        /* compact() is probably most used with a single array of var_names
01522           or multiple string names, rather than a combination of both.
01523           So quickly guess a minimum result size based on that */
01524        if (ZEND_NUM_ARGS() == 1 && Z_TYPE_PP(args[0]) == IS_ARRAY) {
01525               array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(args[0])));
01526        } else {
01527               array_init_size(return_value, ZEND_NUM_ARGS());
01528        }
01529 
01530        for (i=0; i<ZEND_NUM_ARGS(); i++) {
01531               php_compact_var(EG(active_symbol_table), return_value, *args[i] TSRMLS_CC);
01532        }
01533 
01534        if (args) {
01535               efree(args);
01536        }
01537 }
01538 /* }}} */
01539 
01540 /* {{{ proto array array_fill(int start_key, int num, mixed val)
01541    Create an array containing num elements starting with index start_key each initialized to val */
01542 PHP_FUNCTION(array_fill)
01543 {
01544        zval *val;
01545        long start_key, num;
01546 
01547        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llz", &start_key, &num, &val) == FAILURE) {
01548               return;
01549        }
01550 
01551        if (num < 1) {
01552               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements must be positive");
01553               RETURN_FALSE;
01554        }
01555 
01556        /* allocate an array for return */
01557        array_init_size(return_value, num);
01558 
01559        num--;
01560        zval_add_ref(&val);
01561        zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, &val, sizeof(zval *), NULL);
01562 
01563        while (num--) {
01564               zval_add_ref(&val);
01565               zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val, sizeof(zval *), NULL);
01566        }
01567 }
01568 /* }}} */
01569 
01570 /* {{{ proto array array_fill_keys(array keys, mixed val)
01571    Create an array using the elements of the first parameter as keys each initialized to val */
01572 PHP_FUNCTION(array_fill_keys)
01573 {
01574        zval *keys, *val, **entry;
01575        HashPosition pos;
01576 
01577        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &keys, &val) == FAILURE) {
01578               return;
01579        }
01580 
01581        /* Initialize return array */
01582        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
01583 
01584        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
01585        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry, &pos) == SUCCESS) {
01586 
01587               if (Z_TYPE_PP(entry) == IS_LONG) {
01588                      zval_add_ref(&val);
01589                      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &val, sizeof(zval *), NULL);
01590               } else {
01591                      zval key, *key_ptr = *entry;
01592 
01593                      if (Z_TYPE_PP(entry) != IS_STRING) {
01594                             key = **entry;
01595                             zval_copy_ctor(&key);
01596                             convert_to_string(&key);
01597                             key_ptr = &key;
01598                      }
01599 
01600                      zval_add_ref(&val);
01601                      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, &val, sizeof(zval *), NULL);
01602 
01603                      if (key_ptr != *entry) {
01604                             zval_dtor(&key);
01605                      }
01606               }
01607 
01608               zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
01609        }
01610 }
01611 /* }}} */
01612 
01613 /* {{{ proto array range(mixed low, mixed high[, int step])
01614    Create an array containing the range of integers or characters from low to high (inclusive) */
01615 PHP_FUNCTION(range)
01616 {
01617        zval *zlow, *zhigh, *zstep = NULL;
01618        int err = 0, is_step_double = 0;
01619        double step = 1.0;
01620 
01621        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) {
01622               RETURN_FALSE;
01623        }
01624 
01625        if (zstep) {
01626               if (Z_TYPE_P(zstep) == IS_DOUBLE ||
01627                      (Z_TYPE_P(zstep) == IS_STRING && is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0) == IS_DOUBLE)
01628               ) {
01629                      is_step_double = 1;
01630               }
01631 
01632               convert_to_double_ex(&zstep);
01633               step = Z_DVAL_P(zstep);
01634 
01635               /* We only want positive step values. */
01636               if (step < 0.0) {
01637                      step *= -1;
01638               }
01639        }
01640 
01641        /* Initialize the return_value as an array. */
01642        array_init(return_value);
01643 
01644        /* If the range is given as strings, generate an array of characters. */
01645        if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
01646               int type1, type2;
01647               unsigned char *low, *high;
01648               long lstep = (long) step;
01649 
01650               type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
01651               type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
01652 
01653               if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
01654                      goto double_str;
01655               } else if (type1 == IS_LONG || type2 == IS_LONG) {
01656                      goto long_str;
01657               }
01658 
01659               convert_to_string(zlow);
01660               convert_to_string(zhigh);
01661               low = (unsigned char *)Z_STRVAL_P(zlow);
01662               high = (unsigned char *)Z_STRVAL_P(zhigh);
01663 
01664               if (*low > *high) {         /* Negative Steps */
01665                      if (lstep <= 0) {
01666                             err = 1;
01667                             goto err;
01668                      }
01669                      for (; *low >= *high; (*low) -= (unsigned int)lstep) {
01670                             add_next_index_stringl(return_value, (const char *)low, 1, 1);
01671                             if (((signed int)*low - lstep) < 0) {
01672                                    break;
01673                             }
01674                      }
01675               } else if (*high > *low) {  /* Positive Steps */
01676                      if (lstep <= 0) {
01677                             err = 1;
01678                             goto err;
01679                      }
01680                      for (; *low <= *high; (*low) += (unsigned int)lstep) {
01681                             add_next_index_stringl(return_value, (const char *)low, 1, 1);
01682                             if (((signed int)*low + lstep) > 255) {
01683                                    break;
01684                             }
01685                      }
01686               } else {
01687                      add_next_index_stringl(return_value, (const char *)low, 1, 1);
01688               }
01689 
01690        } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
01691               double low, high, value;
01692               long i;
01693 double_str:
01694               convert_to_double(zlow);
01695               convert_to_double(zhigh);
01696               low = Z_DVAL_P(zlow);
01697               high = Z_DVAL_P(zhigh);
01698               i = 0;
01699 
01700               if (low > high) {           /* Negative steps */
01701                      if (low - high < step || step <= 0) {
01702                             err = 1;
01703                             goto err;
01704                      }
01705 
01706                      for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) {
01707                             add_next_index_double(return_value, value);
01708                      }
01709               } else if (high > low) {    /* Positive steps */
01710                      if (high - low < step || step <= 0) {
01711                             err = 1;
01712                             goto err;
01713                      }
01714 
01715                      for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) {
01716                             add_next_index_double(return_value, value);
01717                      }
01718               } else {
01719                      add_next_index_double(return_value, low);
01720               }
01721        } else {
01722               double low, high;
01723               long lstep;
01724 long_str:
01725               convert_to_double(zlow);
01726               convert_to_double(zhigh);
01727               low = Z_DVAL_P(zlow);
01728               high = Z_DVAL_P(zhigh);
01729               lstep = (long) step;
01730 
01731               if (low > high) {           /* Negative steps */
01732                      if (low - high < lstep || lstep <= 0) {
01733                             err = 1;
01734                             goto err;
01735                      }
01736                      for (; low >= high; low -= lstep) {
01737                             add_next_index_long(return_value, (long)low);
01738                      }
01739               } else if (high > low) {    /* Positive steps */
01740                      if (high - low < lstep || lstep <= 0) {
01741                             err = 1;
01742                             goto err;
01743                      }
01744                      for (; low <= high; low += lstep) {
01745                             add_next_index_long(return_value, (long)low);
01746                      }
01747               } else {
01748                      add_next_index_long(return_value, (long)low);
01749               }
01750        }
01751 err:
01752        if (err) {
01753               php_error_docref(NULL TSRMLS_CC, E_WARNING, "step exceeds the specified range");
01754               zval_dtor(return_value);
01755               RETURN_FALSE;
01756        }
01757 }
01758 /* }}} */
01759 
01760 static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */
01761 {
01762        Bucket **elems, *temp;
01763        HashTable *hash;
01764        int j, n_elems, rnd_idx, n_left;
01765 
01766        n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
01767 
01768        if (n_elems < 1) {
01769               return;
01770        }
01771 
01772        elems = (Bucket **)safe_emalloc(n_elems, sizeof(Bucket *), 0);
01773        hash = Z_ARRVAL_P(array);
01774        n_left = n_elems;
01775 
01776        for (j = 0, temp = hash->pListHead; temp; temp = temp->pListNext)
01777               elems[j++] = temp;
01778        while (--n_left) {
01779               rnd_idx = php_rand(TSRMLS_C);
01780               RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
01781               if (rnd_idx != n_left) {
01782                      temp = elems[n_left];
01783                      elems[n_left] = elems[rnd_idx];
01784                      elems[rnd_idx] = temp;
01785               }
01786        }
01787 
01788        HANDLE_BLOCK_INTERRUPTIONS();
01789        hash->pListHead = elems[0];
01790        hash->pListTail = NULL;
01791        hash->pInternalPointer = hash->pListHead;
01792 
01793        for (j = 0; j < n_elems; j++) {
01794               if (hash->pListTail) {
01795                      hash->pListTail->pListNext = elems[j];
01796               }
01797               elems[j]->pListLast = hash->pListTail;
01798               elems[j]->pListNext = NULL;
01799               hash->pListTail = elems[j];
01800        }
01801        temp = hash->pListHead;
01802        j = 0;
01803        while (temp != NULL) {
01804               temp->nKeyLength = 0;
01805               temp->h = j++;
01806               temp = temp->pListNext;
01807        }
01808        hash->nNextFreeElement = n_elems;
01809        zend_hash_rehash(hash);
01810        HANDLE_UNBLOCK_INTERRUPTIONS();
01811 
01812        efree(elems);
01813 }
01814 /* }}} */
01815 
01816 /* {{{ proto bool shuffle(array array_arg)
01817    Randomly shuffle the contents of an array */
01818 PHP_FUNCTION(shuffle)
01819 {
01820        zval *array;
01821 
01822        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
01823               RETURN_FALSE;
01824        }
01825 
01826        php_array_data_shuffle(array TSRMLS_CC);
01827 
01828        RETURN_TRUE;
01829 }
01830 /* }}} */
01831 
01832 PHPAPI HashTable* php_splice(HashTable *in_hash, int offset, int length, zval ***list, int list_count, HashTable **removed) /* {{{ */
01833 {
01834        HashTable     *out_hash = NULL;    /* Output hashtable */
01835        int                   num_in,                    /* Number of entries in the input hashtable */
01836                              pos,                       /* Current position in the hashtable */
01837                              i;                                /* Loop counter */
01838        Bucket        *p;                                /* Pointer to hash bucket */
01839        zval          *entry;                            /* Hash entry */
01840 
01841        /* If input hash doesn't exist, we have nothing to do */
01842        if (!in_hash) {
01843               return NULL;
01844        }
01845 
01846        /* Get number of entries in the input hash */
01847        num_in = zend_hash_num_elements(in_hash);
01848 
01849        /* Clamp the offset.. */
01850        if (offset > num_in) {
01851               offset = num_in;
01852        } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
01853               offset = 0;
01854        }
01855 
01856        /* ..and the length */
01857        if (length < 0) {
01858               length = num_in - offset + length;
01859        } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
01860               length = num_in - offset;
01861        }
01862 
01863        /* Create and initialize output hash */
01864        ALLOC_HASHTABLE(out_hash);
01865        zend_hash_init(out_hash, (length > 0 ? num_in - length : 0) + list_count, NULL, ZVAL_PTR_DTOR, 0);
01866 
01867        /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
01868        for (pos = 0, p = in_hash->pListHead; pos < offset && p ; pos++, p = p->pListNext) {
01869               /* Get entry and increase reference count */
01870               entry = *((zval **)p->pData);
01871               Z_ADDREF_P(entry);
01872 
01873               /* Update output hash depending on key type */
01874               if (p->nKeyLength == 0) {
01875                      zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
01876               } else {
01877                      zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
01878               }
01879        }
01880 
01881        /* If hash for removed entries exists, go until offset+length and copy the entries to it */
01882        if (removed != NULL) {
01883               for ( ; pos < offset + length && p; pos++, p = p->pListNext) {
01884                      entry = *((zval **)p->pData);
01885                      Z_ADDREF_P(entry);
01886                      if (p->nKeyLength == 0) {
01887                             zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
01888                      } else {
01889                             zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
01890                      }
01891               }
01892        } else { /* otherwise just skip those entries */
01893               for ( ; pos < offset + length && p; pos++, p = p->pListNext);
01894        }
01895 
01896        /* If there are entries to insert.. */
01897        if (list != NULL) {
01898               /* ..for each one, create a new zval, copy entry into it and copy it into the output hash */
01899               for (i = 0; i < list_count; i++) {
01900                      entry = *list[i];
01901                      Z_ADDREF_P(entry);
01902                      zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
01903               }
01904        }
01905 
01906        /* Copy the remaining input hash entries to the output hash */
01907        for ( ; p ; p = p->pListNext) {
01908               entry = *((zval **)p->pData);
01909               Z_ADDREF_P(entry);
01910               if (p->nKeyLength == 0) {
01911                      zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
01912               } else {
01913                      zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
01914               }
01915        }
01916 
01917        zend_hash_internal_pointer_reset(out_hash);
01918        return out_hash;
01919 }
01920 /* }}} */
01921 
01922 /* {{{ proto int array_push(array stack, mixed var [, mixed ...])
01923    Pushes elements onto the end of the array */
01924 PHP_FUNCTION(array_push)
01925 {
01926        zval ***args,        /* Function arguments array */
01927                  *stack,           /* Input array */
01928                  *new_var;  /* Variable to be pushed */
01929        int i,                      /* Loop counter */
01930               argc;                /* Number of function arguments */
01931 
01932 
01933        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
01934               return;
01935        }
01936 
01937        /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
01938        for (i = 0; i < argc; i++) {
01939               new_var = *args[i];
01940               Z_ADDREF_P(new_var);
01941 
01942               if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var, sizeof(zval *), NULL) == FAILURE) {
01943                      Z_DELREF_P(new_var);
01944                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
01945                      efree(args);
01946                      RETURN_FALSE;
01947               }
01948        }
01949 
01950        /* Clean up and return the number of values in the stack */
01951        efree(args);
01952        RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
01953 }
01954 /* }}} */
01955 
01956 /* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */
01957 static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
01958 {
01959        zval *stack,  /* Input stack */
01960                **val;              /* Value to be popped */
01961        char *key = NULL;
01962        uint key_len = 0;
01963        ulong index;
01964 
01965        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &stack) == FAILURE) {
01966               return;
01967        }
01968 
01969        if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
01970               return;
01971        }
01972 
01973        /* Get the first or last value and copy it into the return value */
01974        if (off_the_end) {
01975               zend_hash_internal_pointer_end(Z_ARRVAL_P(stack));
01976        } else {
01977               zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
01978        }
01979        zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val);
01980        RETVAL_ZVAL(*val, 1, 0);
01981 
01982        /* Delete the first or last value */
01983        zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL);
01984        if (key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
01985               zend_delete_global_variable(key, key_len - 1 TSRMLS_CC);
01986        } else {
01987               zend_hash_del_key_or_index(Z_ARRVAL_P(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX);
01988        }
01989 
01990        /* If we did a shift... re-index like it did before */
01991        if (!off_the_end) {
01992               unsigned int k = 0;
01993               int should_rehash = 0;
01994               Bucket *p = Z_ARRVAL_P(stack)->pListHead;
01995               while (p != NULL) {
01996                      if (p->nKeyLength == 0) {
01997                             if (p->h != k) {
01998                                    p->h = k++;
01999                                    should_rehash = 1;
02000                             } else {
02001                                    k++;
02002                             }
02003                      }
02004                      p = p->pListNext;
02005               }
02006               Z_ARRVAL_P(stack)->nNextFreeElement = k;
02007               if (should_rehash) {
02008                      zend_hash_rehash(Z_ARRVAL_P(stack));
02009               }
02010        } else if (!key_len && index >= Z_ARRVAL_P(stack)->nNextFreeElement - 1) {
02011               Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
02012        }
02013 
02014        zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
02015 }
02016 /* }}} */
02017 
02018 /* {{{ proto mixed array_pop(array stack)
02019    Pops an element off the end of the array */
02020 PHP_FUNCTION(array_pop)
02021 {
02022        _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
02023 }
02024 /* }}} */
02025 
02026 /* {{{ proto mixed array_shift(array stack)
02027    Pops an element off the beginning of the array */
02028 PHP_FUNCTION(array_shift)
02029 {
02030        _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
02031 }
02032 /* }}} */
02033 
02034 /* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
02035    Pushes elements onto the beginning of the array */
02036 PHP_FUNCTION(array_unshift)
02037 {
02038        zval ***args,               /* Function arguments array */
02039                  *stack;                  /* Input stack */
02040        HashTable *new_hash; /* New hashtable for the stack */
02041        HashTable  old_hash;
02042        int argc;                          /* Number of function arguments */
02043        
02044        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
02045               return;
02046        }
02047 
02048        /* Use splice to insert the elements at the beginning. Destroy old
02049         * hashtable and replace it with new one */
02050        new_hash = php_splice(Z_ARRVAL_P(stack), 0, 0, &args[0], argc, NULL);
02051        old_hash = *Z_ARRVAL_P(stack);
02052        if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
02053               zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
02054        }
02055        *Z_ARRVAL_P(stack) = *new_hash;
02056        FREE_HASHTABLE(new_hash);
02057        zend_hash_destroy(&old_hash);
02058 
02059        /* Clean up and return the number of elements in the stack */
02060        efree(args);
02061        RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
02062 }
02063 /* }}} */
02064 
02065 /* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
02066    Removes the elements designated by offset and length and replace them with supplied array */
02067 PHP_FUNCTION(array_splice)
02068 {
02069        zval *array,                       /* Input array */
02070                *repl_array = NULL, /* Replacement array */
02071                ***repl = NULL;            /* Replacement elements */
02072        HashTable *new_hash = NULL, /* Output array's hash */
02073                 **rem_hash = NULL; /* Removed elements' hash */
02074        HashTable  old_hash;
02075        Bucket *p;                                /* Bucket used for traversing hash */
02076        long   i,
02077                      offset,
02078                      length = 0,
02079                      repl_num = 0;        /* Number of replacement elements */
02080        int           num_in;                            /* Number of elements in the input array */
02081 
02082        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
02083               return;
02084        }
02085 
02086        num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
02087 
02088        if (ZEND_NUM_ARGS() < 3) {
02089               length = num_in;
02090        }
02091 
02092        if (ZEND_NUM_ARGS() == 4) {
02093               /* Make sure the last argument, if passed, is an array */
02094               convert_to_array(repl_array);
02095 
02096               /* Create the array of replacement elements */
02097               repl_num = zend_hash_num_elements(Z_ARRVAL_P(repl_array));
02098               repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0);
02099               for (p = Z_ARRVAL_P(repl_array)->pListHead, i = 0; p; p = p->pListNext, i++) {
02100                      repl[i] = ((zval **)p->pData);
02101               }
02102        }
02103 
02104        /* Don't create the array of removed elements if it's not going
02105         * to be used; e.g. only removing and/or replacing elements */
02106        if (return_value_used) {
02107               int size = length;
02108 
02109               /* Clamp the offset.. */
02110               if (offset > num_in) {
02111                      offset = num_in;
02112               } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
02113                      offset = 0;
02114               }
02115 
02116               /* ..and the length */
02117               if (length < 0) {
02118                      size = num_in - offset + length;
02119               } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
02120                      size = num_in - offset;
02121               }
02122 
02123               /* Initialize return value */
02124               array_init_size(return_value, size > 0 ? size : 0);
02125               rem_hash = &Z_ARRVAL_P(return_value);
02126        }
02127 
02128        /* Perform splice */
02129        new_hash = php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash);
02130 
02131        /* Replace input array's hashtable with the new one */
02132        old_hash = *Z_ARRVAL_P(array);
02133        if (Z_ARRVAL_P(array) == &EG(symbol_table)) {
02134               zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
02135        }
02136        *Z_ARRVAL_P(array) = *new_hash;
02137        FREE_HASHTABLE(new_hash);
02138        zend_hash_destroy(&old_hash);
02139 
02140        /* Clean up */
02141        if (ZEND_NUM_ARGS() == 4) {
02142               efree(repl);
02143        }
02144 }
02145 /* }}} */
02146 
02147 /* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
02148    Returns elements specified by offset and length */
02149 PHP_FUNCTION(array_slice)
02150 {
02151        zval    *input,             /* Input array */
02152                      **z_length = NULL, /* How many elements to get */ 
02153                      **entry;             /* An array entry */
02154        long    offset,             /* Offset to get elements from */
02155                       length = 0;  
02156        zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */
02157        int            num_in,             /* Number of elements in the input array */
02158                       pos;                /* Current position in the array */
02159        char *string_key;
02160        uint string_key_len;
02161        ulong num_key;
02162        HashPosition hpos;
02163 
02164        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|Zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
02165               return;
02166        }
02167 
02168        /* Get number of entries in the input hash */
02169        num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
02170 
02171        /* We want all entries from offset to the end if length is not passed or is null */
02172        if (ZEND_NUM_ARGS() < 3 || Z_TYPE_PP(z_length) == IS_NULL) {
02173               length = num_in;
02174        } else {
02175               convert_to_long_ex(z_length);
02176               length = Z_LVAL_PP(z_length);
02177        }
02178 
02179        /* Clamp the offset.. */
02180        if (offset > num_in) {
02181               array_init(return_value);
02182               return;
02183        } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
02184               offset = 0;
02185        }
02186 
02187        /* ..and the length */
02188        if (length < 0) {
02189               length = num_in - offset + length;
02190        } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
02191               length = num_in - offset;
02192        }
02193 
02194        /* Initialize returned array */
02195        array_init_size(return_value, length > 0 ? length : 0);
02196 
02197        if (length <= 0) {
02198               return;
02199        }
02200 
02201        /* Start at the beginning and go until we hit offset */
02202        pos = 0;
02203        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &hpos);
02204        while (pos < offset && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
02205               pos++;
02206               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
02207        }
02208 
02209        /* Copy elements from input array to the one that's returned */
02210        while (pos < offset + length && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
02211 
02212               zval_add_ref(entry);
02213 
02214               switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &hpos)) {
02215                      case HASH_KEY_IS_STRING:
02216                             zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
02217                             break;
02218 
02219                      case HASH_KEY_IS_LONG:
02220                             if (preserve_keys) {
02221                                    zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
02222                             } else {
02223                                    zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
02224                             }
02225                             break;
02226               }
02227               pos++;
02228               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
02229        }
02230 }
02231 /* }}} */
02232 
02233 PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */
02234 {
02235        zval **src_entry, **dest_entry;
02236        char *string_key;
02237        uint string_key_len;
02238        ulong num_key;
02239        HashPosition pos;
02240 
02241        zend_hash_internal_pointer_reset_ex(src, &pos);
02242        while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) {
02243               switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
02244                      case HASH_KEY_IS_STRING:
02245                             if (recursive && zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == SUCCESS) {
02246                                    HashTable *thash = Z_TYPE_PP(dest_entry) == IS_ARRAY ? Z_ARRVAL_PP(dest_entry) : NULL;
02247 
02248                                    if ((thash && thash->nApplyCount > 1) || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) {
02249                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
02250                                           return 0;
02251                                    }
02252                                    SEPARATE_ZVAL(dest_entry);
02253                                    SEPARATE_ZVAL(src_entry);
02254 
02255                                    if (Z_TYPE_PP(dest_entry) == IS_NULL) {
02256                                           convert_to_array_ex(dest_entry);
02257                                           add_next_index_null(*dest_entry);
02258                                    } else {
02259                                           convert_to_array_ex(dest_entry);
02260                                    }
02261                                    if (Z_TYPE_PP(src_entry) == IS_NULL) {
02262                                           convert_to_array_ex(src_entry);
02263                                           add_next_index_null(*src_entry);
02264                                    } else {
02265                                           convert_to_array_ex(src_entry);
02266                                    }
02267                                    if (thash) {
02268                                           thash->nApplyCount++;
02269                                    }
02270                                    if (!php_array_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry), recursive TSRMLS_CC)) {
02271                                           if (thash) {
02272                                                  thash->nApplyCount--;
02273                                           }
02274                                           return 0;
02275                                    }
02276                                    if (thash) {
02277                                           thash->nApplyCount--;
02278                                    }
02279                             } else {
02280                                    Z_ADDREF_PP(src_entry);
02281                                    zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
02282                             }
02283                             break;
02284 
02285                      case HASH_KEY_IS_LONG:
02286                             Z_ADDREF_PP(src_entry);
02287                             zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL);
02288                             break;
02289               }
02290               zend_hash_move_forward_ex(src, &pos);
02291        }
02292        return 1;
02293 }
02294 /* }}} */
02295 
02296 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */
02297 {
02298        zval **src_entry, **dest_entry;
02299        char *string_key;
02300        uint string_key_len;
02301        ulong num_key;
02302        HashPosition pos;
02303 
02304        for (zend_hash_internal_pointer_reset_ex(src, &pos);
02305             zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS;
02306             zend_hash_move_forward_ex(src, &pos)) {
02307               switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
02308                      case HASH_KEY_IS_STRING:
02309                             if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
02310                                    zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == FAILURE ||
02311                                    Z_TYPE_PP(dest_entry) != IS_ARRAY) {
02312 
02313                                    Z_ADDREF_PP(src_entry);
02314                                    zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
02315 
02316                                    continue;
02317                             }
02318                             break;
02319 
02320                      case HASH_KEY_IS_LONG:
02321                             if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
02322                                    zend_hash_index_find(dest, num_key, (void **)&dest_entry) == FAILURE ||
02323                                    Z_TYPE_PP(dest_entry) != IS_ARRAY) {
02324 
02325                                    Z_ADDREF_PP(src_entry);
02326                                    zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL);
02327 
02328                                    continue;
02329                             }
02330                             break;
02331               }
02332 
02333               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))) {
02334                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
02335                      return 0;
02336               }
02337               SEPARATE_ZVAL(dest_entry);
02338               Z_ARRVAL_PP(dest_entry)->nApplyCount++;
02339               Z_ARRVAL_PP(src_entry)->nApplyCount++;
02340               
02341 
02342               if (!php_array_replace_recursive(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC)) {
02343                      Z_ARRVAL_PP(dest_entry)->nApplyCount--;
02344                      Z_ARRVAL_PP(src_entry)->nApplyCount--;
02345                      return 0;
02346               }
02347               Z_ARRVAL_PP(dest_entry)->nApplyCount--;
02348               Z_ARRVAL_PP(src_entry)->nApplyCount--;
02349        }
02350 
02351        return 1;
02352 }
02353 /* }}} */
02354 
02355 static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
02356 {
02357        zval ***args = NULL;
02358        int argc, i, init_size = 0;
02359 
02360        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
02361               return;
02362        }
02363 
02364        for (i = 0; i < argc; i++) {
02365               if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
02366                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
02367                      efree(args);
02368                      RETURN_NULL();
02369               } else {
02370                      int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i]));
02371 
02372                      if (num > init_size) {
02373                             init_size = num;
02374                      }
02375               }
02376        }
02377 
02378        array_init_size(return_value, init_size);
02379 
02380        for (i = 0; i < argc; i++) {
02381               SEPARATE_ZVAL(args[i]);
02382               if (!replace) {
02383                      php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
02384               } else if (recursive && i > 0) { /* First array will be copied directly instead */
02385                      php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC);
02386               } else {
02387                      zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1);
02388               }
02389        }
02390 
02391        efree(args);
02392 }
02393 /* }}} */
02394 
02395 /* {{{ proto array array_merge(array arr1, array arr2 [, array ...])
02396    Merges elements from passed arrays into one array */
02397 PHP_FUNCTION(array_merge)
02398 {
02399        php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
02400 }
02401 /* }}} */
02402 
02403 /* {{{ proto array array_merge_recursive(array arr1, array arr2 [, array ...])
02404    Recursively merges elements from passed arrays into one array */
02405 PHP_FUNCTION(array_merge_recursive)
02406 {
02407        php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
02408 }
02409 /* }}} */
02410 
02411 /* {{{ proto array array_replace(array arr1, array arr2 [, array ...])
02412    Replaces elements from passed arrays into one array */
02413 PHP_FUNCTION(array_replace)
02414 {
02415        php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
02416 }
02417 /* }}} */
02418 
02419 /* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...])
02420    Recursively replaces elements from passed arrays into one array */
02421 PHP_FUNCTION(array_replace_recursive)
02422 {
02423        php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
02424 }
02425 /* }}} */
02426 
02427 /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
02428    Return just the keys from the input array, optionally only for the specified search_value */
02429 PHP_FUNCTION(array_keys)
02430 {
02431        zval *input,                       /* Input array */
02432             *search_value = NULL,  /* Value to search for */
02433             **entry,                      /* An entry in the input array */
02434               res,                               /* Result of comparison */
02435              *new_val;                           /* New value */
02436        int    add_key;                           /* Flag to indicate whether a key should be added */
02437        char  *string_key;                 /* String key */
02438        uint   string_key_len;
02439        ulong  num_key;                           /* Numeric key */
02440        zend_bool strict = 0;              /* do strict comparison */
02441        HashPosition pos;
02442        int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
02443 
02444        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
02445               return;
02446        }
02447 
02448        if (strict) {
02449               is_equal_func = is_identical_function;
02450        }
02451 
02452        /* Initialize return array */
02453        if (search_value != NULL) {
02454               array_init(return_value);
02455        } else {
02456               array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
02457        }
02458        add_key = 1;
02459 
02460        /* Go through input array and add keys to the return array */
02461        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
02462        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
02463               if (search_value != NULL) {
02464                      is_equal_func(&res, search_value, *entry TSRMLS_CC);
02465                      add_key = zval_is_true(&res);
02466               }
02467 
02468               if (add_key) {
02469                      MAKE_STD_ZVAL(new_val);
02470 
02471                      switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
02472                             case HASH_KEY_IS_STRING:
02473                                    ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
02474                                    zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
02475                                    break;
02476 
02477                             case HASH_KEY_IS_LONG:
02478                                    Z_TYPE_P(new_val) = IS_LONG;
02479                                    Z_LVAL_P(new_val) = num_key;
02480                                    zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
02481                                    break;
02482                      }
02483               }
02484 
02485               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
02486        }
02487 }
02488 /* }}} */
02489 
02490 /* {{{ proto array array_values(array input)
02491    Return just the values from the input array */
02492 PHP_FUNCTION(array_values)
02493 {
02494        zval    *input,             /* Input array */
02495                      **entry;             /* An entry in the input array */
02496        HashPosition pos;
02497 
02498        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
02499               return;
02500        }
02501 
02502        /* Initialize return array */
02503        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
02504 
02505        /* Go through input array and add values to the return array */
02506        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
02507        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
02508               zval_add_ref(entry);
02509               zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
02510               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
02511        }
02512 }
02513 /* }}} */
02514 
02515 /* {{{ proto array array_count_values(array input)
02516    Return the value as key and the frequency of that value in input as value */
02517 PHP_FUNCTION(array_count_values)
02518 {
02519        zval   *input,                     /* Input array */
02520                      **entry,             /* An entry in the input array */
02521                      **tmp;
02522        HashTable *myht;
02523        HashPosition pos;
02524 
02525        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
02526               return;
02527        }
02528 
02529        /* Initialize return array */
02530        array_init(return_value);
02531 
02532        /* Go through input array and add values to the return array */
02533        myht = Z_ARRVAL_P(input);
02534        zend_hash_internal_pointer_reset_ex(myht, &pos);
02535        while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) {
02536               if (Z_TYPE_PP(entry) == IS_LONG) {
02537                      if (zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), (void **)&tmp) == FAILURE) {
02538                             zval *data;
02539                             MAKE_STD_ZVAL(data);
02540                             ZVAL_LONG(data, 1);
02541                             zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
02542                      } else {
02543                             Z_LVAL_PP(tmp)++;
02544                      }
02545               } else if (Z_TYPE_PP(entry) == IS_STRING) {
02546                      if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) {
02547                             zval *data;
02548                             MAKE_STD_ZVAL(data);
02549                             ZVAL_LONG(data, 1);
02550                             zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
02551                      } else {
02552                             Z_LVAL_PP(tmp)++;
02553                      }
02554               } else {
02555                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
02556               }
02557 
02558               zend_hash_move_forward_ex(myht, &pos);
02559        }
02560 }
02561 /* }}} */
02562 
02563 /* {{{ proto array array_reverse(array input [, bool preserve keys])
02564    Return input as a new array with the order of the entries reversed */
02565 PHP_FUNCTION(array_reverse)
02566 {
02567        zval    *input,                           /* Input array */
02568                      **entry;                           /* An entry in the input array */
02569        char    *string_key;
02570        uint     string_key_len;
02571        ulong    num_key;
02572        zend_bool preserve_keys = 0;       /* whether to preserve keys */
02573        HashPosition pos;
02574 
02575        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &input, &preserve_keys) == FAILURE) {
02576               return;
02577        }
02578 
02579        /* Initialize return array */
02580        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
02581 
02582        zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos);
02583        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
02584               zval_add_ref(entry);
02585 
02586               switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) {
02587                      case HASH_KEY_IS_STRING:
02588                             zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
02589                             break;
02590 
02591                      case HASH_KEY_IS_LONG:
02592                             if (preserve_keys) {
02593                                    zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
02594                             } else {
02595                                    zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
02596                             }
02597                             break;
02598               }
02599 
02600               zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos);
02601        }
02602 }
02603 /* }}} */
02604 
02605 /* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
02606    Returns a copy of input array padded with pad_value to size pad_size */
02607 PHP_FUNCTION(array_pad)
02608 {
02609        zval  *input;        /* Input array */
02610        zval  *pad_value;    /* Padding value obviously */
02611        zval ***pads;        /* Array to pass to splice */
02612        HashTable *new_hash;/* Return value from splice */
02613        HashTable  old_hash;
02614        long pad_size;              /* Size to pad to */
02615        long pad_size_abs;   /* Absolute value of pad_size */
02616        int    input_size;          /* Size of the input array */
02617        int    num_pads;            /* How many pads do we need */
02618        int    do_pad;                     /* Whether we should do padding at all */
02619        int    i;
02620 
02621        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "alz", &input, &pad_size, &pad_value) == FAILURE) {
02622               return;
02623        }
02624 
02625        /* Do some initial calculations */
02626        input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
02627        pad_size_abs = abs(pad_size);
02628        if (pad_size_abs < 0) {
02629               php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
02630               zval_dtor(return_value);
02631               RETURN_FALSE;
02632        }
02633        do_pad = (input_size >= pad_size_abs) ? 0 : 1;
02634 
02635        /* Copy the original array */
02636        RETVAL_ZVAL(input, 1, 0);
02637 
02638        /* If no need to pad, no need to continue */
02639        if (!do_pad) {
02640               return;
02641        }
02642 
02643        /* Populate the pads array */
02644        num_pads = pad_size_abs - input_size;
02645        if (num_pads > 1048576) {
02646               php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
02647               zval_dtor(return_value);
02648               RETURN_FALSE;
02649        }
02650        pads = (zval ***)safe_emalloc(num_pads, sizeof(zval **), 0);
02651        for (i = 0; i < num_pads; i++) {
02652               pads[i] = &pad_value;
02653        }
02654 
02655        /* Pad on the right or on the left */
02656        if (pad_size > 0) {
02657               new_hash = php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL);
02658        } else {
02659               new_hash = php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL);
02660        }
02661 
02662        /* Copy the result hash into return value */
02663        old_hash = *Z_ARRVAL_P(return_value);
02664        if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
02665               zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
02666        }
02667        *Z_ARRVAL_P(return_value) = *new_hash;
02668        FREE_HASHTABLE(new_hash);
02669        zend_hash_destroy(&old_hash);
02670 
02671        /* Clean up */
02672        efree(pads);
02673 }
02674 /* }}} */
02675 
02676 /* {{{ proto array array_flip(array input)
02677    Return array with key <-> value flipped */
02678 PHP_FUNCTION(array_flip)
02679 {
02680        zval *array, **entry, *data;
02681        char *string_key;
02682        uint str_key_len;
02683        ulong num_key;
02684        HashPosition pos;
02685 
02686        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
02687               return;
02688        }
02689 
02690        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
02691 
02692        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
02693        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
02694               MAKE_STD_ZVAL(data);
02695               switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 1, &pos)) {
02696                      case HASH_KEY_IS_STRING:
02697                             ZVAL_STRINGL(data, string_key, str_key_len - 1, 0);
02698                             break;
02699                      case HASH_KEY_IS_LONG:
02700                             Z_TYPE_P(data) = IS_LONG;
02701                             Z_LVAL_P(data) = num_key;
02702                             break;
02703               }
02704 
02705               if (Z_TYPE_PP(entry) == IS_LONG) {
02706                      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
02707               } else if (Z_TYPE_PP(entry) == IS_STRING) {
02708                      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
02709               } else {
02710                      zval_ptr_dtor(&data); /* will free also zval structure */
02711                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
02712               }
02713 
02714               zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
02715        }
02716 }
02717 /* }}} */
02718 
02719 /* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
02720    Retuns an array with all string keys lowercased [or uppercased] */
02721 PHP_FUNCTION(array_change_key_case)
02722 {
02723        zval *array, **entry;
02724        char *string_key;
02725        char *new_key;
02726        uint str_key_len;
02727        ulong num_key;
02728        long change_to_upper=0;
02729        HashPosition pos;
02730 
02731        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &change_to_upper) == FAILURE) {
02732               return;
02733        }
02734 
02735        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
02736 
02737        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
02738        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
02739               zval_add_ref(entry);
02740 
02741               switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
02742                      case HASH_KEY_IS_LONG:
02743                             zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(entry), NULL);
02744                             break;
02745                      case HASH_KEY_IS_STRING:
02746                             new_key = estrndup(string_key, str_key_len - 1);
02747                             if (change_to_upper) {
02748                                    php_strtoupper(new_key, str_key_len - 1);
02749                             } else {
02750                                    php_strtolower(new_key, str_key_len - 1);
02751                             }
02752                             zend_hash_update(Z_ARRVAL_P(return_value), new_key, str_key_len, entry, sizeof(entry), NULL);
02753                             efree(new_key);
02754                             break;
02755               }
02756 
02757               zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
02758        }
02759 }
02760 /* }}} */
02761 
02762 /* {{{ proto array array_unique(array input [, int sort_flags])
02763    Removes duplicate values from array */
02764 PHP_FUNCTION(array_unique)
02765 {
02766        zval *array, *tmp;
02767        Bucket *p;
02768        struct bucketindex {
02769               Bucket *b;
02770               unsigned int i;
02771        };
02772        struct bucketindex *arTmp, *cmpdata, *lastkept;
02773        unsigned int i;
02774        long sort_type = PHP_SORT_STRING;
02775 
02776        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
02777               return;
02778        }
02779 
02780        php_set_compare_func(sort_type TSRMLS_CC);
02781 
02782        array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
02783        zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*));
02784 
02785        if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {    /* nothing to do */
02786               return;
02787        }
02788 
02789        /* create and sort array with pointers to the target_hash buckets */
02790        arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent);
02791        if (!arTmp) {
02792               zval_dtor(return_value);
02793               RETURN_FALSE;
02794        }
02795        for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) {
02796               arTmp[i].b = p;
02797               arTmp[i].i = i;
02798        }
02799        arTmp[i].b = NULL;
02800        zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC);
02801 
02802        /* go through the sorted array and delete duplicates from the copy */
02803        lastkept = arTmp;
02804        for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) {
02805               if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
02806                      lastkept = cmpdata;
02807               } else {
02808                      if (lastkept->i > cmpdata->i) {
02809                             p = lastkept->b;
02810                             lastkept = cmpdata;
02811                      } else {
02812                             p = cmpdata->b;
02813                      }
02814                      if (p->nKeyLength == 0) {
02815                             zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
02816                      } else {
02817                             if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
02818                                    zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC);
02819                             } else {
02820                                    zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
02821                             }
02822                      }
02823               }
02824        }
02825        pefree(arTmp, Z_ARRVAL_P(array)->persistent);
02826 }
02827 /* }}} */
02828 
02829 static int zval_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
02830 {
02831        zval result;
02832        zval *first;
02833        zval *second;
02834 
02835        first = *((zval **) a);
02836        second = *((zval **) b);
02837 
02838        if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
02839               return 0;
02840        }
02841 
02842        if (Z_TYPE(result) == IS_DOUBLE) {
02843               if (Z_DVAL(result) < 0) {
02844                      return -1;
02845               } else if (Z_DVAL(result) > 0) {
02846                      return 1;
02847               } else {
02848                      return 0;
02849               }
02850        }
02851 
02852        convert_to_long(&result);
02853 
02854        if (Z_LVAL(result) < 0) {
02855               return -1;
02856        } else if (Z_LVAL(result) > 0) {
02857               return 1;
02858        }
02859 
02860        return 0;
02861 }
02862 /* }}} */
02863 
02864 static int zval_user_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
02865 {
02866        zval **args[2];
02867        zval *retval_ptr;
02868 
02869        args[0] = (zval **) a;
02870        args[1] = (zval **) b;
02871 
02872        BG(user_compare_fci).param_count = 2;
02873        BG(user_compare_fci).params = args;
02874        BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
02875        BG(user_compare_fci).no_separation = 0;
02876 
02877        if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
02878               long retval;
02879 
02880               convert_to_long_ex(&retval_ptr);
02881               retval = Z_LVAL_P(retval_ptr);
02882               zval_ptr_dtor(&retval_ptr);
02883               return retval < 0 ? -1 : retval > 0 ? 1 : 0;;
02884        } else {
02885               return 0;
02886        }
02887 }
02888 /* }}} */
02889 
02890 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
02891 {
02892        Bucket *p;
02893        int argc, i;
02894        zval ***args;
02895        int (*intersect_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
02896        zend_bool ok;
02897        zval **data;
02898        int req_args;
02899        char *param_spec;
02900 
02901        /* Get the argument count */
02902        argc = ZEND_NUM_ARGS();
02903        if (data_compare_type == INTERSECT_COMP_DATA_USER) {
02904               /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
02905               req_args = 3;
02906               param_spec = "+f";
02907               intersect_data_compare_func = zval_user_compare;
02908        } else {
02909               /*     INTERSECT_COMP_DATA_NONE - array_intersect_key()
02910                      INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
02911               req_args = 2;
02912               param_spec = "+";
02913                             
02914               if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
02915                      intersect_data_compare_func = zval_compare;
02916               }
02917        }
02918        
02919        if (argc < req_args) {
02920               php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
02921               return;
02922        }
02923 
02924        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
02925               return;
02926        }
02927 
02928        for (i = 0; i < argc; i++) {
02929               if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
02930                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
02931                      RETVAL_NULL();
02932                      goto out;
02933               }
02934        }
02935 
02936        array_init(return_value);
02937 
02938        for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
02939               if (p->nKeyLength == 0) {
02940                      ok = 1;
02941                      for (i = 1; i < argc; i++) {
02942                             if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == FAILURE ||
02943                                    (intersect_data_compare_func &&
02944                                    intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
02945                             ) {
02946                                    ok = 0;
02947                                    break;
02948                             }
02949                      }
02950                      if (ok) {
02951                             Z_ADDREF_PP((zval**)p->pData);
02952                             zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
02953                      }
02954               } else {
02955                      ok = 1;
02956                      for (i = 1; i < argc; i++) {
02957                             if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == FAILURE ||
02958                                    (intersect_data_compare_func &&
02959                                    intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
02960                             ) {
02961                                    ok = 0;
02962                                    break;
02963                             }
02964                      }
02965                      if (ok) {
02966                             Z_ADDREF_PP((zval**)p->pData);
02967                             zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
02968                      }
02969               }
02970        }
02971 out:
02972        efree(args);
02973 }
02974 /* }}} */
02975 
02976 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
02977 {
02978        zval ***args = NULL;
02979        HashTable *hash;
02980        int arr_argc, i, c = 0;
02981        Bucket ***lists, **list, ***ptrs, *p;
02982        int req_args;
02983        char *param_spec;
02984        zend_fcall_info fci1, fci2;
02985        zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
02986        zend_fcall_info *fci_key, *fci_data;
02987        zend_fcall_info_cache *fci_key_cache, *fci_data_cache;
02988        PHP_ARRAY_CMP_FUNC_VARS;
02989 
02990        int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC);
02991        int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC);
02992 
02993        if (behavior == INTERSECT_NORMAL) {
02994               intersect_key_compare_func = php_array_key_compare;
02995 
02996               if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
02997                      /* array_intersect() */
02998                      req_args = 2;
02999                      param_spec = "+";
03000                      intersect_data_compare_func = php_array_data_compare;
03001               } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
03002                      /* array_uintersect() */
03003                      req_args = 3;
03004                      param_spec = "+f";
03005                      intersect_data_compare_func = php_array_user_compare;
03006               } else {
03007                      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);
03008                      return;
03009               }
03010 
03011               if (ZEND_NUM_ARGS() < req_args) {
03012                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
03013                      return;
03014               }
03015 
03016               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
03017                      return;
03018               }
03019               fci_data = &fci1;
03020               fci_data_cache = &fci1_cache;
03021 
03022        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
03023               /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
03024                * no comparison of the data is done (part of INTERSECT_ASSOC) */
03025               intersect_key_compare_func = php_array_key_compare;
03026 
03027               if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
03028                      /* array_intersect_assoc() or array_intersect_key() */
03029                      req_args = 2;
03030                      param_spec = "+";
03031                      intersect_key_compare_func = php_array_key_compare;
03032                      intersect_data_compare_func = php_array_data_compare;
03033               } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
03034                      /* array_uintersect_assoc() */
03035                      req_args = 3;
03036                      param_spec = "+f";
03037                      intersect_key_compare_func = php_array_key_compare;
03038                      intersect_data_compare_func = php_array_user_compare;
03039                      fci_data = &fci1;
03040                      fci_data_cache = &fci1_cache;
03041               } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
03042                      /* array_intersect_uassoc() or array_intersect_ukey() */
03043                      req_args = 3;
03044                      param_spec = "+f";
03045                      intersect_key_compare_func = php_array_user_key_compare;
03046                      intersect_data_compare_func = php_array_data_compare;
03047                      fci_key = &fci1;
03048                      fci_key_cache = &fci1_cache;
03049               } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
03050                      /* array_uintersect_uassoc() */
03051                      req_args = 4;
03052                      param_spec = "+ff";
03053                      intersect_key_compare_func = php_array_user_key_compare;
03054                      intersect_data_compare_func = php_array_user_compare;
03055                      fci_data = &fci1;
03056                      fci_data_cache = &fci1_cache;
03057                      fci_key = &fci2;
03058                      fci_key_cache = &fci2_cache;
03059               } else {
03060                      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);
03061                      return;
03062               }
03063 
03064               if (ZEND_NUM_ARGS() < req_args) {
03065                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
03066                      return;
03067               }
03068 
03069               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
03070                      return;
03071               }
03072 
03073        } else {
03074               php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
03075               return;
03076        }
03077 
03078        PHP_ARRAY_CMP_FUNC_BACKUP();
03079 
03080        /* for each argument, create and sort list with pointers to the hash buckets */
03081        lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
03082        ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
03083        php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
03084 
03085        if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
03086               BG(user_compare_fci) = *fci_data;
03087               BG(user_compare_fci_cache) = *fci_data_cache;
03088        } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
03089               BG(user_compare_fci) = *fci_key;
03090               BG(user_compare_fci_cache) = *fci_key_cache;
03091        }
03092 
03093        for (i = 0; i < arr_argc; i++) {
03094               if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
03095                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
03096                      arr_argc = i; /* only free up to i - 1 */
03097                      goto out;
03098               }
03099               hash = Z_ARRVAL_PP(args[i]);
03100               list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
03101               if (!list) {
03102                      PHP_ARRAY_CMP_FUNC_RESTORE();
03103 
03104                      efree(ptrs);
03105                      efree(lists);
03106                      efree(args);
03107                      RETURN_FALSE;
03108               }
03109               lists[i] = list;
03110               ptrs[i] = list;
03111               for (p = hash->pListHead; p; p = p->pListNext) {
03112                      *list++ = p;
03113               }
03114               *list = NULL;
03115               if (behavior == INTERSECT_NORMAL) {
03116                      zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC);
03117               } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
03118                      zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC);
03119               }
03120        }
03121 
03122        /* copy the argument array */
03123        RETVAL_ZVAL(*args[0], 1, 0);
03124        if (return_value->value.ht == &EG(symbol_table)) {
03125               HashTable *ht;
03126               zval *tmp;
03127 
03128               ALLOC_HASHTABLE(ht);
03129               zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
03130               zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
03131               return_value->value.ht = ht;
03132        }
03133 
03134        /* go through the lists and look for common values */
03135        while (*ptrs[0]) {
03136               if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
03137                      &&
03138                      key_compare_type == INTERSECT_COMP_KEY_USER) {
03139 
03140                      BG(user_compare_fci) = *fci_key;
03141                      BG(user_compare_fci_cache) = *fci_key_cache;
03142               }
03143 
03144               for (i = 1; i < arr_argc; i++) {
03145                      if (behavior & INTERSECT_NORMAL) {
03146                             while (*ptrs[i] && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
03147                                    ptrs[i]++;
03148                             }
03149                      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
03150                             while (*ptrs[i] && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
03151                                    ptrs[i]++;
03152                             }
03153                             if ((!c && *ptrs[i]) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
03154                                    /* this means that ptrs[i] is not NULL so we can compare
03155                                     * and "c==0" is from last operation
03156                                     * in this branch of code we enter only when INTERSECT_ASSOC
03157                                     * since when we have INTERSECT_KEY compare of data is not wanted. */
03158                                    if (data_compare_type == INTERSECT_COMP_DATA_USER) {
03159                                           BG(user_compare_fci) = *fci_data;
03160                                           BG(user_compare_fci_cache) = *fci_data_cache;
03161                                    }
03162                                    if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) {
03163                                           c = 1;
03164                                           if (key_compare_type == INTERSECT_COMP_KEY_USER) {
03165                                                  BG(user_compare_fci) = *fci_key;
03166                                                  BG(user_compare_fci_cache) = *fci_key_cache;
03167                                                  /* When KEY_USER, the last parameter is always the callback */
03168                                           }
03169                                           /* we are going to the break */
03170                                    } else {
03171                                           /* continue looping */
03172                                    }
03173                             }
03174                      }
03175                      if (!*ptrs[i]) {
03176                             /* delete any values corresponding to remains of ptrs[0] */
03177                             /* and exit because they do not present in at least one of */
03178                             /* the other arguments */
03179                             for (;;) {
03180                                    p = *ptrs[0]++;
03181                                    if (!p) {
03182                                           goto out;
03183                                    }
03184                                    if (p->nKeyLength == 0) {
03185                                           zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
03186                                    } else {
03187                                           zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
03188                                    }
03189                             }
03190                      }
03191                      if (c) /* here we get if not all are equal */
03192                             break;
03193                      ptrs[i]++;
03194               }
03195               if (c) {
03196                      /* Value of ptrs[0] not in all arguments, delete all entries */
03197                      /* with value < value of ptrs[i] */
03198                      for (;;) {
03199                             p = *ptrs[0];
03200                             if (p->nKeyLength == 0) {
03201                                    zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
03202                             } else {
03203                                    zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
03204                             }
03205                             if (!*++ptrs[0]) {
03206                                    goto out;
03207                             }
03208                             if (behavior == INTERSECT_NORMAL) {
03209                                    if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) {
03210                                           break;
03211                                    }
03212                             } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
03213                                    /* no need of looping because indexes are unique */
03214                                    break;
03215                             }
03216                      }
03217               } else {
03218                      /* ptrs[0] is present in all the arguments */
03219                      /* Skip all entries with same value as ptrs[0] */
03220                      for (;;) {
03221                             if (!*++ptrs[0]) {
03222                                    goto out;
03223                             }
03224                             if (behavior == INTERSECT_NORMAL) {
03225                                    if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
03226                                           break;
03227                                    }
03228                             } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
03229                                    /* no need of looping because indexes are unique */
03230                                    break;
03231                             }
03232                      }
03233               }
03234        }
03235 out:
03236        for (i = 0; i < arr_argc; i++) {
03237               hash = Z_ARRVAL_PP(args[i]);
03238               pefree(lists[i], hash->persistent);
03239        }
03240 
03241        PHP_ARRAY_CMP_FUNC_RESTORE();
03242 
03243        efree(ptrs);
03244        efree(lists);
03245        efree(args);
03246 }
03247 /* }}} */
03248 
03249 /* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
03250    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. */
03251 PHP_FUNCTION(array_intersect_key)
03252 {
03253        php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
03254 }
03255 /* }}} */
03256 
03257 /* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
03258    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. */
03259 PHP_FUNCTION(array_intersect_ukey)
03260 {
03261        php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
03262 }
03263 /* }}} */
03264 
03265 /* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
03266    Returns the entries of arr1 that have values which are present in all the other arguments */
03267 PHP_FUNCTION(array_intersect)
03268 {
03269        php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
03270 }
03271 /* }}} */
03272 
03273 /* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
03274    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. */
03275 PHP_FUNCTION(array_uintersect)
03276 {
03277        php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
03278 }
03279 /* }}} */
03280 
03281 /* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
03282    Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
03283 PHP_FUNCTION(array_intersect_assoc)
03284 {
03285        php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
03286 }
03287 /* }}} */
03288 
03289 /* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
03290    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. */
03291 PHP_FUNCTION(array_intersect_uassoc)
03292 {
03293        php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
03294 }
03295 /* }}} */
03296 
03297 /* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
03298    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. */
03299 PHP_FUNCTION(array_uintersect_assoc)
03300 {
03301        php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
03302 }
03303 /* }}} */
03304 
03305 /* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
03306    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. */
03307 PHP_FUNCTION(array_uintersect_uassoc)
03308 {
03309        php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
03310 }
03311 /* }}} */
03312 
03313 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
03314 {
03315        Bucket *p;
03316        int argc, i;
03317        zval ***args;
03318        int (*diff_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
03319        zend_bool ok;
03320        zval **data;
03321 
03322        /* Get the argument count */
03323        argc = ZEND_NUM_ARGS();
03324        if (data_compare_type == DIFF_COMP_DATA_USER) {
03325               if (argc < 3) {
03326                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
03327                      return;
03328               }
03329               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
03330                      return;
03331               }
03332               diff_data_compare_func = zval_user_compare;
03333        } else {
03334               if (argc < 2) {
03335                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
03336                      return;
03337               }
03338               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
03339                      return;
03340               }
03341               if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
03342                      diff_data_compare_func = zval_compare;
03343               }
03344        }
03345 
03346        for (i = 0; i < argc; i++) {
03347               if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
03348                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
03349                      RETVAL_NULL();
03350                      goto out;
03351               }
03352        }
03353 
03354        array_init(return_value);
03355 
03356        for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
03357               if (p->nKeyLength == 0) {
03358                      ok = 1;
03359                      for (i = 1; i < argc; i++) {
03360                             if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS &&
03361                                    (!diff_data_compare_func ||
03362                                    diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
03363                             ) {
03364                                    ok = 0;
03365                                    break;
03366                             }
03367                      }
03368                      if (ok) {
03369                             Z_ADDREF_PP((zval**)p->pData);
03370                             zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
03371                      }
03372               } else {
03373                      ok = 1;
03374                      for (i = 1; i < argc; i++) {
03375                             if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS &&
03376                                    (!diff_data_compare_func ||
03377                                    diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
03378                             ) {
03379                                    ok = 0;
03380                                    break;
03381                             }
03382                      }
03383                      if (ok) {
03384                             Z_ADDREF_PP((zval**)p->pData);
03385                             zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
03386                      }
03387               }
03388        }
03389 out:
03390        efree(args);
03391 }
03392 /* }}} */
03393 
03394 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
03395 {
03396        zval ***args = NULL;
03397        HashTable *hash;
03398        int arr_argc, i, c;
03399        Bucket ***lists, **list, ***ptrs, *p;
03400        int req_args;
03401        char *param_spec;
03402        zend_fcall_info fci1, fci2;
03403        zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
03404        zend_fcall_info *fci_key, *fci_data;
03405        zend_fcall_info_cache *fci_key_cache, *fci_data_cache;
03406        PHP_ARRAY_CMP_FUNC_VARS;
03407 
03408        int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC);
03409        int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC);
03410 
03411        if (behavior == DIFF_NORMAL) {
03412               diff_key_compare_func = php_array_key_compare;
03413 
03414               if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
03415                      /* array_diff */
03416                      req_args = 2;
03417                      param_spec = "+";
03418                      diff_data_compare_func = php_array_data_compare;
03419               } else if (data_compare_type == DIFF_COMP_DATA_USER) {
03420                      /* array_udiff */
03421                      req_args = 3;
03422                      param_spec = "+f";
03423                      diff_data_compare_func = php_array_user_compare;
03424               } else {
03425                      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);
03426                      return;
03427               }
03428 
03429               if (ZEND_NUM_ARGS() < req_args) {
03430                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
03431                      return;
03432               }
03433 
03434               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
03435                      return;
03436               }
03437               fci_data = &fci1;
03438               fci_data_cache = &fci1_cache;
03439 
03440        } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
03441               /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
03442                * no comparison of the data is done (part of DIFF_ASSOC) */
03443 
03444               if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
03445                      /* array_diff_assoc() or array_diff_key() */
03446                      req_args = 2;
03447                      param_spec = "+";
03448                      diff_key_compare_func = php_array_key_compare;
03449                      diff_data_compare_func = php_array_data_compare;
03450               } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
03451                      /* array_udiff_assoc() */
03452                      req_args = 3;
03453                      param_spec = "+f";
03454                      diff_key_compare_func = php_array_key_compare;
03455                      diff_data_compare_func = php_array_user_compare;
03456                      fci_data = &fci1;
03457                      fci_data_cache = &fci1_cache;
03458               } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
03459                      /* array_diff_uassoc() or array_diff_ukey() */
03460                      req_args = 3;
03461                      param_spec = "+f";
03462                      diff_key_compare_func = php_array_user_key_compare;
03463                      diff_data_compare_func = php_array_data_compare;
03464                      fci_key = &fci1;
03465                      fci_key_cache = &fci1_cache;
03466               } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
03467                      /* array_udiff_uassoc() */
03468                      req_args = 4;
03469                      param_spec = "+ff";
03470                      diff_key_compare_func = php_array_user_key_compare;
03471                      diff_data_compare_func = php_array_user_compare;
03472                      fci_data = &fci1;
03473                      fci_data_cache = &fci1_cache;
03474                      fci_key = &fci2;
03475                      fci_key_cache = &fci2_cache;
03476               } else {
03477                      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);
03478                      return;
03479               }
03480 
03481               if (ZEND_NUM_ARGS() < req_args) {
03482                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
03483                      return;
03484               }
03485 
03486               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
03487                      return;
03488               }
03489 
03490        } else {
03491               php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
03492               return;
03493        }
03494 
03495        PHP_ARRAY_CMP_FUNC_BACKUP();
03496 
03497        /* for each argument, create and sort list with pointers to the hash buckets */
03498        lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
03499        ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
03500        php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
03501 
03502        if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
03503               BG(user_compare_fci) = *fci_data;
03504               BG(user_compare_fci_cache) = *fci_data_cache;
03505        } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
03506               BG(user_compare_fci) = *fci_key;
03507               BG(user_compare_fci_cache) = *fci_key_cache;
03508        }
03509 
03510        for (i = 0; i < arr_argc; i++) {
03511               if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
03512                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
03513                      arr_argc = i; /* only free up to i - 1 */
03514                      goto out;
03515               }
03516               hash = Z_ARRVAL_PP(args[i]);
03517               list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
03518               if (!list) {
03519                      PHP_ARRAY_CMP_FUNC_RESTORE();
03520 
03521                      efree(ptrs);
03522                      efree(lists);
03523                      efree(args);
03524                      RETURN_FALSE;
03525               }
03526               lists[i] = list;
03527               ptrs[i] = list;
03528               for (p = hash->pListHead; p; p = p->pListNext) {
03529                      *list++ = p;
03530               }
03531               *list = NULL;
03532               if (behavior == DIFF_NORMAL) {
03533                      zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_data_compare_func TSRMLS_CC);
03534               } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
03535                      zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_key_compare_func TSRMLS_CC);
03536               }
03537        }
03538 
03539        /* copy the argument array */
03540        RETVAL_ZVAL(*args[0], 1, 0);
03541        if (return_value->value.ht == &EG(symbol_table)) {
03542               HashTable *ht;
03543               zval *tmp;
03544 
03545               ALLOC_HASHTABLE(ht);
03546               zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
03547               zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
03548               return_value->value.ht = ht;
03549        }
03550 
03551        /* go through the lists and look for values of ptr[0] that are not in the others */
03552        while (*ptrs[0]) {
03553               if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
03554                      &&
03555                      key_compare_type == DIFF_COMP_KEY_USER
03556               ) {
03557                      BG(user_compare_fci) = *fci_key;
03558                      BG(user_compare_fci_cache) = *fci_key_cache;
03559               }
03560               c = 1;
03561               for (i = 1; i < arr_argc; i++) {
03562                      Bucket **ptr = ptrs[i];
03563                      if (behavior == DIFF_NORMAL) {
03564                             while (*ptrs[i] && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
03565                                    ptrs[i]++;
03566                             }
03567                      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
03568                             while (*ptr && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
03569                                    ptr++;
03570                             }
03571                      }
03572                      if (!c) {
03573                             if (behavior == DIFF_NORMAL) {
03574                                    if (*ptrs[i]) {
03575                                           ptrs[i]++;
03576                                    }
03577                                    break;
03578                             } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
03579                                    /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
03580                                     * data comparison is not needed - skipped. */
03581                                    if (*ptr) {
03582                                           if (data_compare_type == DIFF_COMP_DATA_USER) {
03583                                                  BG(user_compare_fci) = *fci_data;
03584                                                  BG(user_compare_fci_cache) = *fci_data_cache;
03585                                           }
03586                                           if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) {
03587                                                  /* the data is not the same */
03588                                                  c = -1;
03589                                                  if (key_compare_type == DIFF_COMP_KEY_USER) {
03590                                                         BG(user_compare_fci) = *fci_key;
03591                                                         BG(user_compare_fci_cache) = *fci_key_cache;
03592                                                  }
03593                                           } else {
03594                                                  break;
03595                                                  /* we have found the element in other arrays thus we don't want it
03596                                                   * in the return_value -> delete from there */
03597                                           }
03598                                    }
03599                             } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
03600                                    /* the behavior here differs from INTERSECT_KEY in php_intersect
03601                                     * since in the "diff" case we have to remove the entry from
03602                                     * return_value while when doing intersection the entry must not
03603                                     * be deleted. */
03604                                    break; /* remove the key */
03605                             }
03606                      }
03607               }
03608               if (!c) {
03609                      /* ptrs[0] in one of the other arguments */
03610                      /* delete all entries with value as ptrs[0] */
03611                      for (;;) {
03612                             p = *ptrs[0];
03613                             if (p->nKeyLength == 0) {
03614                                    zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
03615                             } else {
03616                                    zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
03617                             }
03618                             if (!*++ptrs[0]) {
03619                                    goto out;
03620                             }
03621                             if (behavior == DIFF_NORMAL) {
03622                                    if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
03623                                           break;
03624                                    }
03625                             } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
03626                                    /* in this case no array_key_compare is needed */
03627                                    break;
03628                             }
03629                      }
03630               } else {
03631                      /* ptrs[0] in none of the other arguments */
03632                      /* skip all entries with value as ptrs[0] */
03633                      for (;;) {
03634                             if (!*++ptrs[0]) {
03635                                    goto out;
03636                             }
03637                             if (behavior == DIFF_NORMAL) {
03638                                    if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
03639                                           break;
03640                                    }
03641                             } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
03642                                    /* in this case no array_key_compare is needed */
03643                                    break;
03644                             }
03645                      }
03646               }
03647        }
03648 out:
03649        for (i = 0; i < arr_argc; i++) {
03650               hash = Z_ARRVAL_PP(args[i]);
03651               pefree(lists[i], hash->persistent);
03652        }
03653 
03654        PHP_ARRAY_CMP_FUNC_RESTORE();
03655 
03656        efree(ptrs);
03657        efree(lists);
03658        efree(args);
03659 }
03660 /* }}} */
03661 
03662 /* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
03663    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. */
03664 PHP_FUNCTION(array_diff_key)
03665 {
03666        php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
03667 }
03668 /* }}} */
03669 
03670 /* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
03671    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. */
03672 PHP_FUNCTION(array_diff_ukey)
03673 {
03674        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
03675 }
03676 /* }}} */
03677 
03678 /* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
03679    Returns the entries of arr1 that have values which are not present in any of the others arguments. */
03680 PHP_FUNCTION(array_diff)
03681 {
03682        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
03683 }
03684 /* }}} */
03685 
03686 /* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
03687    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. */
03688 PHP_FUNCTION(array_udiff)
03689 {
03690        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
03691 }
03692 /* }}} */
03693 
03694 /* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
03695    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 */
03696 PHP_FUNCTION(array_diff_assoc)
03697 {
03698        php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
03699 }
03700 /* }}} */
03701 
03702 /* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
03703    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. */
03704 PHP_FUNCTION(array_diff_uassoc)
03705 {
03706        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
03707 }
03708 /* }}} */
03709 
03710 /* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
03711    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. */
03712 PHP_FUNCTION(array_udiff_assoc)
03713 {
03714        php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
03715 }
03716 /* }}} */
03717 
03718 /* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
03719    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. */
03720 PHP_FUNCTION(array_udiff_uassoc)
03721 {
03722        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
03723 }
03724 /* }}} */
03725 
03726 #define MULTISORT_ORDER     0
03727 #define MULTISORT_TYPE      1
03728 #define MULTISORT_LAST      2
03729 
03730 PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
03731 {
03732        Bucket **ab = *(Bucket ***)a;
03733        Bucket **bb = *(Bucket ***)b;
03734        int r;
03735        int result = 0;
03736        zval temp;
03737 
03738        r = 0;
03739        do {
03740               php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC);
03741 
03742               ARRAYG(compare_func)(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData) TSRMLS_CC);
03743               result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp);
03744               if (result != 0) {
03745                      return result;
03746               }
03747               r++;
03748        } while (ab[r] != NULL);
03749 
03750        return result;
03751 }
03752 /* }}} */
03753 
03754 #define MULTISORT_ABORT                                        \
03755        for (k = 0; k < MULTISORT_LAST; k++)      \
03756               efree(ARRAYG(multisort_flags)[k]); \
03757        efree(arrays);                                                 \
03758        efree(args);                                            \
03759        RETURN_FALSE;
03760 
03761 /* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]], ...])
03762    Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
03763 PHP_FUNCTION(array_multisort)
03764 {
03765        zval***                     args;
03766        zval***                     arrays;
03767        Bucket***            indirect;
03768        Bucket*                     p;
03769        HashTable*           hash;
03770        int                         argc;
03771        int                         array_size;
03772        int                         num_arrays = 0;
03773        int                         parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
03774        int                         sort_order = PHP_SORT_ASC;
03775        int                         sort_type  = PHP_SORT_REGULAR;
03776        int                         i, k;
03777 
03778        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
03779               return;
03780        }
03781 
03782        /* Allocate space for storing pointers to input arrays and sort flags. */
03783        arrays = (zval ***)ecalloc(argc, sizeof(zval **));
03784        for (i = 0; i < MULTISORT_LAST; i++) {
03785               parse_state[i] = 0;
03786               ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
03787        }
03788 
03789        /* Here we go through the input arguments and parse them. Each one can
03790         * be either an array or a sort flag which follows an array. If not
03791         * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
03792         * accordingly. There can't be two sort flags of the same type after an
03793         * array, and the very first argument has to be an array. */
03794        for (i = 0; i < argc; i++) {
03795               if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
03796                      /* We see the next array, so we update the sort flags of
03797                       * the previous array and reset the sort flags. */
03798                      if (i > 0) {
03799                             ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
03800                             ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
03801                             sort_order = PHP_SORT_ASC;
03802                             sort_type = PHP_SORT_REGULAR;
03803                      }
03804                      arrays[num_arrays++] = args[i];
03805 
03806                      /* Next one may be an array or a list of sort flags. */
03807                      for (k = 0; k < MULTISORT_LAST; k++) {
03808                             parse_state[k] = 1;
03809                      }
03810               } else if (Z_TYPE_PP(args[i]) == IS_LONG) {
03811                      switch (Z_LVAL_PP(args[i])) {
03812                             case PHP_SORT_ASC:
03813                             case PHP_SORT_DESC:
03814                                    /* flag allowed here */
03815                                    if (parse_state[MULTISORT_ORDER] == 1) {
03816                                           /* Save the flag and make sure then next arg is not the current flag. */
03817                                           sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1;
03818                                           parse_state[MULTISORT_ORDER] = 0;
03819                                    } else {
03820                                           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);
03821                                           MULTISORT_ABORT;
03822                                    }
03823                                    break;
03824 
03825                             case PHP_SORT_REGULAR:
03826                             case PHP_SORT_NUMERIC:
03827                             case PHP_SORT_STRING:
03828 #if HAVE_STRCOLL
03829                             case PHP_SORT_LOCALE_STRING:
03830 #endif
03831                                    /* flag allowed here */
03832                                    if (parse_state[MULTISORT_TYPE] == 1) {
03833                                           /* Save the flag and make sure then next arg is not the current flag. */
03834                                           sort_type = Z_LVAL_PP(args[i]);
03835                                           parse_state[MULTISORT_TYPE] = 0;
03836                                    } else {
03837                                           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);
03838                                           MULTISORT_ABORT;
03839                                    }
03840                                    break;
03841 
03842                             default:
03843                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
03844                                    MULTISORT_ABORT;
03845                                    break;
03846 
03847                      }
03848               } else {
03849                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
03850                      MULTISORT_ABORT;
03851               }
03852        }
03853        /* Take care of the last array sort flags. */
03854        ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
03855        ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
03856 
03857        /* Make sure the arrays are of the same size. */
03858        array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0]));
03859        for (i = 0; i < num_arrays; i++) {
03860               if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) {
03861                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
03862                      MULTISORT_ABORT;
03863               }
03864        }
03865 
03866        /* If all arrays are empty we don't need to do anything. */
03867        if (array_size < 1) {
03868               for (k = 0; k < MULTISORT_LAST; k++) {
03869                      efree(ARRAYG(multisort_flags)[k]);
03870               }
03871               efree(arrays);
03872               efree(args);
03873               RETURN_TRUE;
03874        }
03875 
03876        /* Create the indirection array. This array is of size MxN, where
03877         * M is the number of entries in each input array and N is the number
03878         * of the input arrays + 1. The last column is NULL to indicate the end
03879         * of the row. */
03880        indirect = (Bucket ***)safe_emalloc(array_size, sizeof(Bucket **), 0);
03881        for (i = 0; i < array_size; i++) {
03882               indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0);
03883        }
03884        for (i = 0; i < num_arrays; i++) {
03885               k = 0;
03886               for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) {
03887                      indirect[k][i] = p;
03888               }
03889        }
03890        for (k = 0; k < array_size; k++) {
03891               indirect[k][num_arrays] = NULL;
03892        }
03893 
03894        /* Do the actual sort magic - bada-bim, bada-boom. */
03895        zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
03896 
03897        /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
03898        HANDLE_BLOCK_INTERRUPTIONS();
03899        for (i = 0; i < num_arrays; i++) {
03900               hash = Z_ARRVAL_PP(arrays[i]);
03901               hash->pListHead = indirect[0][i];;
03902               hash->pListTail = NULL;
03903               hash->pInternalPointer = hash->pListHead;
03904 
03905               for (k = 0; k < array_size; k++) {
03906                      if (hash->pListTail) {
03907                             hash->pListTail->pListNext = indirect[k][i];
03908                      }
03909                      indirect[k][i]->pListLast = hash->pListTail;
03910                      indirect[k][i]->pListNext = NULL;
03911                      hash->pListTail = indirect[k][i];
03912               }
03913 
03914               p = hash->pListHead;
03915               k = 0;
03916               while (p != NULL) {
03917                      if (p->nKeyLength == 0)
03918                             p->h = k++;
03919                      p = p->pListNext;
03920               }
03921               hash->nNextFreeElement = array_size;
03922               zend_hash_rehash(hash);
03923        }
03924        HANDLE_UNBLOCK_INTERRUPTIONS();
03925 
03926        /* Clean up. */
03927        for (i = 0; i < array_size; i++) {
03928               efree(indirect[i]);
03929        }
03930        efree(indirect);
03931        for (k = 0; k < MULTISORT_LAST; k++) {
03932               efree(ARRAYG(multisort_flags)[k]);
03933        }
03934        efree(arrays);
03935        efree(args);
03936        RETURN_TRUE;
03937 }
03938 /* }}} */
03939 
03940 /* {{{ proto mixed array_rand(array input [, int num_req])
03941    Return key/keys for random entry/entries in the array */
03942 PHP_FUNCTION(array_rand)
03943 {
03944        zval *input;
03945        long randval, num_req = 1;
03946        int num_avail, key_type;
03947        char *string_key;
03948        uint string_key_len;
03949        ulong num_key;
03950        HashPosition pos;
03951 
03952        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) {
03953               return;
03954        }
03955 
03956        num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
03957 
03958        if (ZEND_NUM_ARGS() > 1) {
03959               if (num_req <= 0 || num_req > num_avail) {
03960                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
03961                      return;
03962               }
03963        }
03964 
03965        /* Make the return value an array only if we need to pass back more than one result. */
03966        if (num_req > 1) {
03967               array_init_size(return_value, num_req);
03968        }
03969 
03970        /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
03971        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
03972        while (num_req && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
03973 
03974               randval = php_rand(TSRMLS_C);
03975 
03976               if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
03977                      /* If we are returning a single result, just do it. */
03978                      if (Z_TYPE_P(return_value) != IS_ARRAY) {
03979                             if (key_type == HASH_KEY_IS_STRING) {
03980                                    RETURN_STRINGL(string_key, string_key_len - 1, 1);
03981                             } else {
03982                                    RETURN_LONG(num_key);
03983                             }
03984                      } else {
03985                             /* Append the result to the return value. */
03986                             if (key_type == HASH_KEY_IS_STRING) {
03987                                    add_next_index_stringl(return_value, string_key, string_key_len - 1, 1);
03988                             } else {
03989                                    add_next_index_long(return_value, num_key);
03990                             }
03991                      }
03992                      num_req--;
03993               }
03994               num_avail--;
03995               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
03996        }
03997 }
03998 /* }}} */
03999 
04000 /* {{{ proto mixed array_sum(array input)
04001    Returns the sum of the array entries */
04002 PHP_FUNCTION(array_sum)
04003 {
04004        zval *input,
04005                **entry,
04006                entry_n;
04007        HashPosition pos;
04008        double dval;
04009 
04010        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
04011               return;
04012        }
04013 
04014        ZVAL_LONG(return_value, 0);
04015 
04016        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
04017               zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
04018               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
04019        ) {
04020               if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
04021                      continue;
04022               }
04023               entry_n = **entry;
04024               zval_copy_ctor(&entry_n);
04025               convert_scalar_to_number(&entry_n TSRMLS_CC);
04026 
04027               if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
04028                      dval = (double)Z_LVAL_P(return_value) + (double)Z_LVAL(entry_n);
04029                      if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) {
04030                             Z_LVAL_P(return_value) += Z_LVAL(entry_n);
04031                             continue;
04032                      }
04033               }
04034               convert_to_double(return_value);
04035               convert_to_double(&entry_n);
04036               Z_DVAL_P(return_value) += Z_DVAL(entry_n);
04037        }
04038 }
04039 /* }}} */
04040 
04041 /* {{{ proto mixed array_product(array input)
04042    Returns the product of the array entries */
04043 PHP_FUNCTION(array_product)
04044 {
04045        zval *input,
04046                **entry,
04047                entry_n;
04048        HashPosition pos;
04049        double dval;
04050 
04051        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
04052               return;
04053        }
04054 
04055        ZVAL_LONG(return_value, 1);
04056        if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
04057               return;
04058        }
04059 
04060        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
04061               zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
04062               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
04063        ) {
04064               if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
04065                      continue;
04066               }
04067               entry_n = **entry;
04068               zval_copy_ctor(&entry_n);
04069               convert_scalar_to_number(&entry_n TSRMLS_CC);
04070 
04071               if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
04072                      dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
04073                      if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) {
04074                             Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
04075                             continue;
04076                      }
04077               }
04078               convert_to_double(return_value);
04079               convert_to_double(&entry_n);
04080               Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
04081        }
04082 }
04083 /* }}} */
04084 
04085 /* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
04086    Iteratively reduce the array to a single value via the callback. */
04087 PHP_FUNCTION(array_reduce)
04088 {
04089        zval *input;
04090        zval **args[2];
04091        zval **operand;
04092        zval *result = NULL;
04093        zval *retval;
04094        zend_fcall_info fci;
04095        zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
04096        zval *initial = NULL;
04097        HashPosition pos;
04098        HashTable *htbl;
04099 
04100        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
04101               return;
04102        }
04103 
04104        if (ZEND_NUM_ARGS() > 2) {
04105               ALLOC_ZVAL(result);
04106               MAKE_COPY_ZVAL(&initial, result);
04107        } else {
04108               MAKE_STD_ZVAL(result);
04109               ZVAL_NULL(result);
04110        }
04111 
04112        /* (zval **)input points to an element of argument stack
04113         * the base pointer of which is subject to change.
04114         * thus we need to keep the pointer to the hashtable for safety */
04115        htbl = Z_ARRVAL_P(input);
04116 
04117        if (zend_hash_num_elements(htbl) == 0) {
04118               if (result) {
04119                      RETVAL_ZVAL(result, 1, 1);
04120               }
04121               return;
04122        }
04123 
04124        fci.retval_ptr_ptr = &retval;
04125        fci.param_count = 2;
04126        fci.no_separation = 0;
04127 
04128        zend_hash_internal_pointer_reset_ex(htbl, &pos);
04129        while (zend_hash_get_current_data_ex(htbl, (void **)&operand, &pos) == SUCCESS) {
04130 
04131               if (result) {
04132                      args[0] = &result;
04133                      args[1] = operand;
04134                      fci.params = args;
04135 
04136                      if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
04137                             zval_ptr_dtor(&result);
04138                             result = retval;
04139                      } else {
04140                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback");
04141                             return;
04142                      }
04143               } else {
04144                      result = *operand;
04145                      zval_add_ref(&result);
04146               }
04147               zend_hash_move_forward_ex(htbl, &pos);
04148        }
04149        RETVAL_ZVAL(result, 1, 1);
04150 }
04151 /* }}} */
04152 
04153 /* {{{ proto array array_filter(array input [, mixed callback])
04154    Filters elements from the array via the callback. */
04155 PHP_FUNCTION(array_filter)
04156 {
04157        zval *array;
04158        zval **operand;
04159        zval **args[1];
04160        zval *retval = NULL;
04161        zend_bool have_callback = 0;
04162        char *string_key;
04163        zend_fcall_info fci = empty_fcall_info;
04164        zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
04165        uint string_key_len;
04166        ulong num_key;
04167        HashPosition pos;
04168 
04169        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|f", &array, &fci, &fci_cache) == FAILURE) {
04170               return;
04171        }
04172 
04173        array_init(return_value);
04174        if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
04175               return;
04176        }
04177 
04178        if (ZEND_NUM_ARGS() > 1) {
04179               have_callback = 1;
04180               fci.no_separation = 0;
04181               fci.retval_ptr_ptr = &retval;
04182               fci.param_count = 1;
04183        }
04184 
04185        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
04186               zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&operand, &pos) == SUCCESS;
04187               zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
04188        ) {
04189               if (have_callback) {
04190                      args[0] = operand;
04191                      fci.params = args;
04192 
04193                      if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
04194                             if (!zend_is_true(retval)) {
04195                                    zval_ptr_dtor(&retval);
04196                                    continue;
04197                             } else {
04198                                    zval_ptr_dtor(&retval);
04199                             }
04200                      } else {
04201                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
04202                             return;
04203                      }
04204               } else if (!zend_is_true(*operand)) {
04205                      continue;
04206               }
04207 
04208               zval_add_ref(operand);
04209               switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos)) {
04210                      case HASH_KEY_IS_STRING:
04211                             zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
04212                             break;
04213 
04214                      case HASH_KEY_IS_LONG:
04215                             zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand, sizeof(zval *), NULL);
04216                             break;
04217               }
04218        }
04219 }
04220 /* }}} */
04221 
04222 /* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
04223    Applies the callback to the elements in given arrays. */
04224 PHP_FUNCTION(array_map)
04225 {
04226        zval ***arrays = NULL;
04227        int n_arrays = 0;
04228        zval ***params;
04229        zval *result, *null;
04230        HashPosition *array_pos;
04231        zval **args;
04232        zend_fcall_info fci = empty_fcall_info;
04233        zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
04234        int i, k, maxlen = 0;
04235        int *array_len;
04236 
04237        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) {
04238               return;
04239        }
04240 
04241        RETVAL_NULL();
04242 
04243        args = (zval **)safe_emalloc(n_arrays, sizeof(zval *), 0);
04244        array_len = (int *)safe_emalloc(n_arrays, sizeof(int), 0);
04245        array_pos = (HashPosition *)safe_emalloc(n_arrays, sizeof(HashPosition), 0);
04246 
04247        for (i = 0; i < n_arrays; i++) {
04248               if (Z_TYPE_PP(arrays[i]) != IS_ARRAY) {
04249                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2);
04250                      efree(arrays);
04251                      efree(args);
04252                      efree(array_len);
04253                      efree(array_pos);
04254                      return;
04255               }
04256               SEPARATE_ZVAL_IF_NOT_REF(arrays[i]);
04257               args[i] = *arrays[i];
04258               array_len[i] = zend_hash_num_elements(Z_ARRVAL_PP(arrays[i]));
04259               if (array_len[i] > maxlen) {
04260                      maxlen = array_len[i];
04261               }
04262               zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(arrays[i]), &array_pos[i]);
04263        }
04264 
04265        efree(arrays);
04266 
04267        /* Short-circuit: if no callback and only one array, just return it. */
04268        if (!ZEND_FCI_INITIALIZED(fci) && n_arrays == 1) {
04269               RETVAL_ZVAL(args[0], 1, 0);
04270               efree(array_len);
04271               efree(array_pos);
04272               efree(args);
04273               return;
04274        }
04275 
04276        array_init_size(return_value, maxlen);
04277        params = (zval ***)safe_emalloc(n_arrays, sizeof(zval **), 0);
04278        MAKE_STD_ZVAL(null);
04279        ZVAL_NULL(null);
04280 
04281        /* We iterate through all the arrays at once. */
04282        for (k = 0; k < maxlen; k++) {
04283               uint str_key_len;
04284               ulong num_key;
04285               char *str_key;
04286               int key_type = 0;
04287 
04288               /* If no callback, the result will be an array, consisting of current
04289                * entries from all arrays. */
04290               if (!ZEND_FCI_INITIALIZED(fci)) {
04291                      MAKE_STD_ZVAL(result);
04292                      array_init_size(result, n_arrays);
04293               }
04294 
04295               for (i = 0; i < n_arrays; i++) {
04296                      /* If this array still has elements, add the current one to the
04297                       * parameter list, otherwise use null value. */
04298                      if (k < array_len[i]) {
04299                             zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), (void **)&params[i], &array_pos[i]);
04300 
04301                             /* It is safe to store only last value of key type, because
04302                              * this loop will run just once if there is only 1 array. */
04303                             if (n_arrays == 1) {
04304                                    key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(args[0]), &str_key, &str_key_len, &num_key, 0, &array_pos[i]);
04305                             }
04306                             zend_hash_move_forward_ex(Z_ARRVAL_P(args[i]), &array_pos[i]);
04307                      } else {
04308                             params[i] = &null;
04309                      }
04310 
04311                      if (!ZEND_FCI_INITIALIZED(fci)) {
04312                             zval_add_ref(params[i]);
04313                             add_next_index_zval(result, *params[i]);
04314                      }
04315               }
04316 
04317               if (ZEND_FCI_INITIALIZED(fci)) {
04318                      fci.retval_ptr_ptr = &result;
04319                      fci.param_count = n_arrays;
04320                      fci.params = params;
04321                      fci.no_separation = 0;
04322 
04323                      if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || !result) {
04324                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
04325                             efree(array_len);
04326                             efree(args);
04327                             efree(array_pos);
04328                             zval_dtor(return_value);
04329                             zval_ptr_dtor(&null);
04330                             efree(params);
04331                             RETURN_NULL();
04332                      }
04333               }
04334 
04335               if (n_arrays > 1) {
04336                      add_next_index_zval(return_value, result);
04337               } else {
04338                      if (key_type == HASH_KEY_IS_STRING) {
04339                             add_assoc_zval_ex(return_value, str_key, str_key_len, result);
04340                      } else {
04341                             add_index_zval(return_value, num_key, result);
04342                      }
04343               }
04344        }
04345 
04346        zval_ptr_dtor(&null);
04347        efree(params);
04348        efree(array_len);
04349        efree(array_pos);
04350        efree(args);
04351 }
04352 /* }}} */
04353 
04354 /* {{{ proto bool array_key_exists(mixed key, array search)
04355    Checks if the given key or index exists in the array */
04356 PHP_FUNCTION(array_key_exists)
04357 {
04358        zval *key;                                /* key to check for */
04359        HashTable *array;                  /* array to check in */
04360 
04361        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zH", &key, &array) == FAILURE) {
04362               return;
04363        }
04364 
04365        switch (Z_TYPE_P(key)) {
04366               case IS_STRING:
04367                      if (zend_symtable_exists(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1)) {
04368                             RETURN_TRUE;
04369                      }
04370                      RETURN_FALSE;
04371               case IS_LONG:
04372                      if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
04373                             RETURN_TRUE;
04374                      }
04375                      RETURN_FALSE;
04376               case IS_NULL:
04377                      if (zend_hash_exists(array, "", 1)) {
04378                             RETURN_TRUE;
04379                      }
04380                      RETURN_FALSE;
04381 
04382               default:
04383                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
04384                      RETURN_FALSE;
04385        }
04386 }
04387 /* }}} */
04388 
04389 /* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
04390    Split array into chunks */
04391 PHP_FUNCTION(array_chunk)
04392 {
04393        int argc = ZEND_NUM_ARGS(), key_type, num_in;
04394        long size, current = 0;
04395        char *str_key;
04396        uint str_key_len;
04397        ulong num_key;
04398        zend_bool preserve_keys = 0;
04399        zval *input = NULL;
04400        zval *chunk = NULL;
04401        zval **entry;
04402        HashPosition pos;
04403 
04404        if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) {
04405               return;
04406        }
04407        /* Do bounds checking for size parameter. */
04408        if (size < 1) {
04409               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0");
04410               return;
04411        }
04412 
04413        num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
04414 
04415        if (size > num_in) {
04416               size = num_in > 0 ? num_in : 1;
04417        }
04418 
04419        array_init_size(return_value, ((num_in - 1) / size) + 1);
04420 
04421        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
04422        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&entry, &pos) == SUCCESS) {
04423               /* If new chunk, create and initialize it. */
04424               if (!chunk) {
04425                      MAKE_STD_ZVAL(chunk);
04426                      array_init_size(chunk, size);
04427               }
04428 
04429               /* Add entry to the chunk, preserving keys if necessary. */
04430               zval_add_ref(entry);
04431 
04432               if (preserve_keys) {
04433                      key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &str_key, &str_key_len, &num_key, 0, &pos);
04434                      switch (key_type) {
04435                             case HASH_KEY_IS_STRING:
04436                                    add_assoc_zval_ex(chunk, str_key, str_key_len, *entry);
04437                                    break;
04438                             default:
04439                                    add_index_zval(chunk, num_key, *entry);
04440                                    break;
04441                      }
04442               } else {
04443                      add_next_index_zval(chunk, *entry);
04444               }
04445 
04446               /* If reached the chunk size, add it to the result array, and reset the
04447                * pointer. */
04448               if (!(++current % size)) {
04449                      add_next_index_zval(return_value, chunk);
04450                      chunk = NULL;
04451               }
04452 
04453               zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
04454        }
04455 
04456        /* Add the final chunk if there is one. */
04457        if (chunk) {
04458               add_next_index_zval(return_value, chunk);
04459        }
04460 }
04461 /* }}} */
04462 
04463 /* {{{ proto array array_combine(array keys, array values)
04464    Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
04465 PHP_FUNCTION(array_combine)
04466 {
04467        zval *values, *keys;
04468        HashPosition pos_values, pos_keys;
04469        zval **entry_keys, **entry_values;
04470        int num_keys, num_values;
04471 
04472        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) {
04473               return;
04474        }
04475 
04476        num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys));
04477        num_values = zend_hash_num_elements(Z_ARRVAL_P(values));
04478 
04479        if (num_keys != num_values) {
04480               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements");
04481               RETURN_FALSE;
04482        }
04483 
04484        if (!num_keys) {
04485               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have at least 1 element");
04486               RETURN_FALSE;
04487        }
04488 
04489        array_init_size(return_value, num_keys);
04490 
04491        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos_keys);
04492        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
04493        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry_keys, &pos_keys) == SUCCESS &&
04494               zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&entry_values, &pos_values) == SUCCESS
04495        ) {
04496               if (Z_TYPE_PP(entry_keys) == IS_LONG) {
04497                      zval_add_ref(entry_values);
04498                      add_index_zval(return_value, Z_LVAL_PP(entry_keys), *entry_values);
04499               } else {
04500                      zval key, *key_ptr = *entry_keys;
04501 
04502                      if (Z_TYPE_PP(entry_keys) != IS_STRING) {
04503                             key = **entry_keys;
04504                             zval_copy_ctor(&key);
04505                             convert_to_string(&key);
04506                             key_ptr = &key;
04507                      }
04508 
04509                      zval_add_ref(entry_values);
04510                      add_assoc_zval_ex(return_value, Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, *entry_values);
04511 
04512                      if (key_ptr != *entry_keys) {
04513                             zval_dtor(&key);
04514                      }
04515               }
04516 
04517               zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos_keys);
04518               zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
04519        }
04520 }
04521 /* }}} */
04522 
04523 /*
04524  * Local variables:
04525  * tab-width: 4
04526  * c-basic-offset: 4
04527  * End:
04528  * vim600: noet sw=4 ts=4 fdm=marker
04529  * vim<600: noet sw=4 ts=4
04530  */