root/ext/standard/array.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_DECLARE_MODULE_GLOBALS
  2. PHP_MINIT_FUNCTION
  3. PHP_MSHUTDOWN_FUNCTION
  4. php_set_compare_func
  5. php_array_key_compare
  6. php_array_reverse_key_compare
  7. PHP_FUNCTION
  8. PHP_FUNCTION
  9. php_count_recursive
  10. PHP_FUNCTION
  11. php_array_data_compare
  12. php_array_reverse_data_compare
  13. php_array_natural_general_compare
  14. php_array_natural_compare
  15. php_array_natural_case_compare
  16. php_natsort
  17. PHP_FUNCTION
  18. PHP_FUNCTION
  19. PHP_FUNCTION
  20. PHP_FUNCTION
  21. PHP_FUNCTION
  22. PHP_FUNCTION
  23. php_array_user_compare
  24. PHP_FUNCTION
  25. PHP_FUNCTION
  26. php_array_user_key_compare
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. PHP_FUNCTION
  30. PHP_FUNCTION
  31. PHP_FUNCTION
  32. PHP_FUNCTION
  33. PHP_FUNCTION
  34. PHP_FUNCTION
  35. PHP_FUNCTION
  36. php_array_walk
  37. PHP_FUNCTION
  38. PHP_FUNCTION
  39. php_search_array
  40. PHP_FUNCTION
  41. PHP_FUNCTION
  42. php_valid_var_name
  43. php_prefix_varname
  44. PHP_FUNCTION
  45. php_compact_var
  46. PHP_FUNCTION
  47. PHP_FUNCTION
  48. PHP_FUNCTION
  49. PHP_FUNCTION
  50. php_array_data_shuffle
  51. PHP_FUNCTION
  52. php_splice
  53. PHP_FUNCTION
  54. _phpi_pop
  55. PHP_FUNCTION
  56. PHP_FUNCTION
  57. PHP_FUNCTION
  58. PHP_FUNCTION
  59. PHP_FUNCTION
  60. php_array_merge
  61. php_array_replace_recursive
  62. php_array_merge_or_replace_wrapper
  63. PHP_FUNCTION
  64. PHP_FUNCTION
  65. PHP_FUNCTION
  66. PHP_FUNCTION
  67. PHP_FUNCTION
  68. PHP_FUNCTION
  69. PHP_FUNCTION
  70. array_column_param_helper
  71. PHP_FUNCTION
  72. PHP_FUNCTION
  73. PHP_FUNCTION
  74. PHP_FUNCTION
  75. PHP_FUNCTION
  76. PHP_FUNCTION
  77. zval_compare
  78. zval_user_compare
  79. php_array_intersect_key
  80. php_array_intersect
  81. PHP_FUNCTION
  82. PHP_FUNCTION
  83. PHP_FUNCTION
  84. PHP_FUNCTION
  85. PHP_FUNCTION
  86. PHP_FUNCTION
  87. PHP_FUNCTION
  88. PHP_FUNCTION
  89. php_array_diff_key
  90. php_array_diff
  91. PHP_FUNCTION
  92. PHP_FUNCTION
  93. PHP_FUNCTION
  94. PHP_FUNCTION
  95. PHP_FUNCTION
  96. PHP_FUNCTION
  97. PHP_FUNCTION
  98. PHP_FUNCTION
  99. php_multisort_compare
  100. PHP_FUNCTION
  101. PHP_FUNCTION
  102. PHP_FUNCTION
  103. PHP_FUNCTION
  104. PHP_FUNCTION
  105. PHP_FUNCTION
  106. PHP_FUNCTION
  107. PHP_FUNCTION
  108. PHP_FUNCTION
  109. PHP_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */