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_array_key_compare
  5. php_array_reverse_key_compare
  6. php_array_key_compare_numeric
  7. php_array_reverse_key_compare_numeric
  8. php_array_key_compare_string_case
  9. php_array_reverse_key_compare_string_case
  10. php_array_key_compare_string
  11. php_array_reverse_key_compare_string
  12. php_array_key_compare_string_natural_general
  13. php_array_key_compare_string_natural_case
  14. php_array_reverse_key_compare_string_natural_case
  15. php_array_key_compare_string_natural
  16. php_array_reverse_key_compare_string_natural
  17. php_array_key_compare_string_locale
  18. php_array_reverse_key_compare_string_locale
  19. php_array_data_compare
  20. php_array_reverse_data_compare
  21. php_array_data_compare_numeric
  22. php_array_reverse_data_compare_numeric
  23. php_array_data_compare_string_case
  24. php_array_reverse_data_compare_string_case
  25. php_array_data_compare_string
  26. php_array_reverse_data_compare_string
  27. php_array_natural_general_compare
  28. php_array_natural_compare
  29. php_array_reverse_natural_compare
  30. php_array_natural_case_compare
  31. php_array_reverse_natural_case_compare
  32. php_array_data_compare_string_locale
  33. php_array_reverse_data_compare_string_locale
  34. php_get_key_compare_func
  35. php_get_data_compare_func
  36. PHP_FUNCTION
  37. PHP_FUNCTION
  38. php_count_recursive
  39. PHP_FUNCTION
  40. php_natsort
  41. PHP_FUNCTION
  42. PHP_FUNCTION
  43. PHP_FUNCTION
  44. PHP_FUNCTION
  45. PHP_FUNCTION
  46. PHP_FUNCTION
  47. php_array_user_compare
  48. php_usort
  49. PHP_FUNCTION
  50. PHP_FUNCTION
  51. php_array_user_key_compare
  52. PHP_FUNCTION
  53. PHP_FUNCTION
  54. PHP_FUNCTION
  55. PHP_FUNCTION
  56. PHP_FUNCTION
  57. PHP_FUNCTION
  58. PHP_FUNCTION
  59. PHP_FUNCTION
  60. PHP_FUNCTION
  61. php_array_walk
  62. PHP_FUNCTION
  63. PHP_FUNCTION
  64. php_search_array
  65. PHP_FUNCTION
  66. PHP_FUNCTION
  67. php_valid_var_name
  68. php_prefix_varname
  69. PHP_FUNCTION
  70. php_compact_var
  71. PHP_FUNCTION
  72. PHP_FUNCTION
  73. PHP_FUNCTION
  74. PHP_FUNCTION
  75. php_array_data_shuffle
  76. PHP_FUNCTION
  77. php_splice
  78. PHP_FUNCTION
  79. PHP_FUNCTION
  80. PHP_FUNCTION
  81. PHP_FUNCTION
  82. PHP_FUNCTION
  83. PHP_FUNCTION
  84. php_array_merge_recursive
  85. php_array_merge
  86. php_array_replace_recursive
  87. php_array_merge_or_replace_wrapper
  88. PHP_FUNCTION
  89. PHP_FUNCTION
  90. PHP_FUNCTION
  91. PHP_FUNCTION
  92. PHP_FUNCTION
  93. PHP_FUNCTION
  94. PHP_FUNCTION
  95. array_column_param_helper
  96. array_column_fetch_prop
  97. PHP_FUNCTION
  98. PHP_FUNCTION
  99. PHP_FUNCTION
  100. PHP_FUNCTION
  101. PHP_FUNCTION
  102. array_bucketindex_swap
  103. PHP_FUNCTION
  104. zval_compare
  105. zval_user_compare
  106. php_array_intersect_key
  107. php_array_intersect
  108. PHP_FUNCTION
  109. PHP_FUNCTION
  110. PHP_FUNCTION
  111. PHP_FUNCTION
  112. PHP_FUNCTION
  113. PHP_FUNCTION
  114. PHP_FUNCTION
  115. PHP_FUNCTION
  116. php_array_diff_key
  117. php_array_diff
  118. PHP_FUNCTION
  119. PHP_FUNCTION
  120. PHP_FUNCTION
  121. PHP_FUNCTION
  122. PHP_FUNCTION
  123. PHP_FUNCTION
  124. PHP_FUNCTION
  125. PHP_FUNCTION
  126. php_multisort_compare
  127. array_bucket_p_sawp
  128. PHP_FUNCTION
  129. PHP_FUNCTION
  130. PHP_FUNCTION
  131. PHP_FUNCTION
  132. PHP_FUNCTION
  133. PHP_FUNCTION
  134. PHP_FUNCTION
  135. PHP_FUNCTION
  136. PHP_FUNCTION
  137. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   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 "zend_smart_str.h"
  49 #ifdef HAVE_SPL
  50 #include "ext/spl/spl_array.h"
  51 #endif
  52 
  53 /* {{{ defines */
  54 #define EXTR_OVERWRITE                  0
  55 #define EXTR_SKIP                               1
  56 #define EXTR_PREFIX_SAME                2
  57 #define EXTR_PREFIX_ALL                 3
  58 #define EXTR_PREFIX_INVALID             4
  59 #define EXTR_PREFIX_IF_EXISTS   5
  60 #define EXTR_IF_EXISTS                  6
  61 
  62 #define EXTR_REFS                               0x100
  63 
  64 #define CASE_LOWER                              0
  65 #define CASE_UPPER                              1
  66 
  67 #define DIFF_NORMAL                     1
  68 #define DIFF_KEY                        2
  69 #define DIFF_ASSOC                      6
  70 #define DIFF_COMP_DATA_NONE    -1
  71 #define DIFF_COMP_DATA_INTERNAL 0
  72 #define DIFF_COMP_DATA_USER     1
  73 #define DIFF_COMP_KEY_INTERNAL  0
  74 #define DIFF_COMP_KEY_USER      1
  75 
  76 #define INTERSECT_NORMAL                1
  77 #define INTERSECT_KEY                   2
  78 #define INTERSECT_ASSOC                 6
  79 #define INTERSECT_COMP_DATA_NONE    -1
  80 #define INTERSECT_COMP_DATA_INTERNAL 0
  81 #define INTERSECT_COMP_DATA_USER     1
  82 #define INTERSECT_COMP_KEY_INTERNAL  0
  83 #define INTERSECT_COMP_KEY_USER      1
  84 /* }}} */
  85 
  86 ZEND_DECLARE_MODULE_GLOBALS(array)
  87 
  88 /* {{{ php_array_init_globals
  89 */
  90 static void php_array_init_globals(zend_array_globals *array_globals)
  91 {
  92         memset(array_globals, 0, sizeof(zend_array_globals));
  93 }
  94 /* }}} */
  95 
  96 PHP_MINIT_FUNCTION(array) /* {{{ */
  97 {
  98         ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
  99 
 100         REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
 101         REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
 102         REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
 103         REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
 104         REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
 105         REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
 106         REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
 107         REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
 108 
 109         REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
 110         REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
 111 
 112         REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
 113         REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
 114         REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
 115         REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
 116         REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
 117         REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
 118 
 119         REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
 120         REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
 121 
 122         REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
 123         REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
 124 
 125         REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
 126         REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
 127 
 128         return SUCCESS;
 129 }
 130 /* }}} */
 131 
 132 PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
 133 {
 134 #ifdef ZTS
 135         ts_free_id(array_globals_id);
 136 #endif
 137 
 138         return SUCCESS;
 139 }
 140 /* }}} */
 141 
 142 static int php_array_key_compare(const void *a, const void *b) /* {{{ */
 143 {
 144         Bucket *f = (Bucket *) a;
 145         Bucket *s = (Bucket *) b;
 146         zend_uchar t;
 147         zend_long l1, l2;
 148         double d;
 149 
 150         if (f->key == NULL) {
 151                 if (s->key == NULL) {
 152                         return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
 153                 } else {
 154                         l1 = (zend_long)f->h;
 155                         t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
 156                         if (t == IS_LONG) {
 157                                 /* pass */
 158                         } else if (t == IS_DOUBLE) {
 159                                 return ZEND_NORMALIZE_BOOL((double)l1 - d);
 160                         } else {
 161                                 l2 = 0;
 162                         }
 163                 }
 164         } else {
 165                 if (s->key) {
 166                         return zendi_smart_strcmp(f->key, s->key);
 167                 } else {
 168                         l2 = (zend_long)s->h;
 169                         t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
 170                         if (t == IS_LONG) {
 171                                 /* pass */
 172                         } else if (t == IS_DOUBLE) {
 173                                 return ZEND_NORMALIZE_BOOL(d - (double)l2);
 174                         } else {
 175                                 l1 = 0;
 176                         }
 177                 }
 178         }
 179         return l1 > l2 ? 1 : (l1 < l2 ? -1 : 0);
 180 }
 181 /* }}} */
 182 
 183 static int php_array_reverse_key_compare(const void *a, const void *b) /* {{{ */
 184 {
 185         return php_array_key_compare(b, a);
 186 }
 187 /* }}} */
 188 
 189 static int php_array_key_compare_numeric(const void *a, const void *b) /* {{{ */
 190 {
 191         Bucket *f = (Bucket *) a;
 192         Bucket *s = (Bucket *) b;
 193 
 194         if (f->key == NULL && s->key == NULL) {
 195                 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
 196         } else {
 197                 double d1, d2;
 198                 if (f->key) {
 199                         d1 = zend_strtod(f->key->val, NULL);
 200                 } else {
 201                         d1 = (double)(zend_long)f->h;
 202                 }
 203                 if (s->key) {
 204                         d2 = zend_strtod(s->key->val, NULL);
 205                 } else {
 206                         d2 = (double)(zend_long)s->h;
 207                 }
 208                 return ZEND_NORMALIZE_BOOL(d1 - d2);
 209         }
 210 }
 211 /* }}} */
 212 
 213 static int php_array_reverse_key_compare_numeric(const void *a, const void *b) /* {{{ */
 214 {
 215         return php_array_key_compare_numeric(b, a);
 216 }
 217 /* }}} */
 218 
 219 static int php_array_key_compare_string_case(const void *a, const void *b) /* {{{ */
 220 {
 221         Bucket *f = (Bucket *) a;
 222         Bucket *s = (Bucket *) b;
 223         char *s1, *s2;
 224         size_t l1, l2;
 225         char buf1[MAX_LENGTH_OF_LONG + 1];
 226         char buf2[MAX_LENGTH_OF_LONG + 1];
 227 
 228         if (f->key) {
 229                 s1 = f->key->val;
 230                 l1 = f->key->len;
 231         } else {
 232                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
 233                 l1 = buf1 + sizeof(buf1) - 1 - s1;
 234         }
 235         if (s->key) {
 236                 s2 = s->key->val;
 237                 l2 = s->key->len;
 238         } else {
 239                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
 240                 l2 = buf2 + sizeof(buf2) - 1 - s1;
 241         }
 242         return zend_binary_strcasecmp_l(s1, l1, s2, l2);
 243 }
 244 /* }}} */
 245 
 246 static int php_array_reverse_key_compare_string_case(const void *a, const void *b) /* {{{ */
 247 {
 248         return php_array_key_compare_string_case(b, a);
 249 }
 250 /* }}} */
 251 
 252 static int php_array_key_compare_string(const void *a, const void *b) /* {{{ */
 253 {
 254         Bucket *f = (Bucket *) a;
 255         Bucket *s = (Bucket *) b;
 256         char *s1, *s2;
 257         size_t l1, l2;
 258         char buf1[MAX_LENGTH_OF_LONG + 1];
 259         char buf2[MAX_LENGTH_OF_LONG + 1];
 260 
 261         if (f->key) {
 262                 s1 = f->key->val;
 263                 l1 = f->key->len;
 264         } else {
 265                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
 266                 l1 = buf1 + sizeof(buf1) - 1 - s1;
 267         }
 268         if (s->key) {
 269                 s2 = s->key->val;
 270                 l2 = s->key->len;
 271         } else {
 272                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
 273                 l2 = buf2 + sizeof(buf2) - 1 - s2;
 274         }
 275         return zend_binary_strcmp(s1, l1, s2, l2);
 276 }
 277 /* }}} */
 278 
 279 static int php_array_reverse_key_compare_string(const void *a, const void *b) /* {{{ */
 280 {
 281         return php_array_key_compare_string(b, a);
 282 }
 283 /* }}} */
 284 
 285 static int php_array_key_compare_string_natural_general(const void *a, const void *b, int fold_case) /* {{{ */
 286 {
 287         Bucket *f = (Bucket *) a;
 288         Bucket *s = (Bucket *) b;
 289         char *s1, *s2;
 290         size_t l1, l2;
 291         char buf1[MAX_LENGTH_OF_LONG + 1];
 292         char buf2[MAX_LENGTH_OF_LONG + 1];
 293 
 294         if (f->key) {
 295                 s1 = f->key->val;
 296                 l1 = f->key->len;
 297         } else {
 298                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
 299                 l1 = buf1 + sizeof(buf1) - 1 - s1;
 300         }
 301         if (s->key) {
 302                 s2 = s->key->val;
 303                 l2 = s->key->len;
 304         } else {
 305                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
 306                 l2 = buf2 + sizeof(buf2) - 1 - s1;
 307         }
 308         return strnatcmp_ex(s1, l1, s2, l2, fold_case);
 309 }
 310 /* }}} */
 311 
 312 static int php_array_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */
 313 {
 314         return php_array_key_compare_string_natural_general(a, b, 1);
 315 }
 316 /* }}} */
 317 
 318 static int php_array_reverse_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */
 319 {
 320         return php_array_key_compare_string_natural_general(b, a, 1);
 321 }
 322 /* }}} */
 323 
 324 static int php_array_key_compare_string_natural(const void *a, const void *b) /* {{{ */
 325 {
 326         return php_array_key_compare_string_natural_general(a, b, 0);
 327 }
 328 /* }}} */
 329 
 330 static int php_array_reverse_key_compare_string_natural(const void *a, const void *b) /* {{{ */
 331 {
 332         return php_array_key_compare_string_natural_general(b, a, 0);
 333 }
 334 /* }}} */
 335 
 336 #if HAVE_STRCOLL
 337 static int php_array_key_compare_string_locale(const void *a, const void *b) /* {{{ */
 338 {
 339         Bucket *f = (Bucket *) a;
 340         Bucket *s = (Bucket *) b;
 341         char *s1, *s2;
 342         char buf1[MAX_LENGTH_OF_LONG + 1];
 343         char buf2[MAX_LENGTH_OF_LONG + 1];
 344 
 345         if (f->key) {
 346                 s1 = f->key->val;
 347         } else {
 348                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
 349         }
 350         if (s->key) {
 351                 s2 = s->key->val;
 352         } else {
 353                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
 354         }
 355         return strcoll(s1, s2);
 356 }
 357 /* }}} */
 358 
 359 static int php_array_reverse_key_compare_string_locale(const void *a, const void *b) /* {{{ */
 360 {
 361         return php_array_key_compare_string_locale(b, a);
 362 }
 363 /* }}} */
 364 #endif
 365 
 366 /* Numbers are always smaller than strings int this function as it
 367  * anyway doesn't make much sense to compare two different data types.
 368  * This keeps it consistent and simple.
 369  *
 370  * This is not correct any more, depends on what compare_func is set to.
 371  */
 372 static int php_array_data_compare(const void *a, const void *b) /* {{{ */
 373 {
 374         Bucket *f;
 375         Bucket *s;
 376         zval result;
 377         zval *first;
 378         zval *second;
 379 
 380         f = (Bucket *) a;
 381         s = (Bucket *) b;
 382 
 383         first = &f->val;
 384         second = &s->val;
 385 
 386         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
 387                 first = Z_INDIRECT_P(first);
 388         }
 389         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
 390                 second = Z_INDIRECT_P(second);
 391         }
 392         if (compare_function(&result, first, second) == FAILURE) {
 393                 return 0;
 394         }
 395 
 396         ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
 397         return Z_LVAL(result);
 398 }
 399 /* }}} */
 400 
 401 static int php_array_reverse_data_compare(const void *a, const void *b) /* {{{ */
 402 {
 403         return php_array_data_compare(a, b) * -1;
 404 }
 405 /* }}} */
 406 
 407 static int php_array_data_compare_numeric(const void *a, const void *b) /* {{{ */
 408 {
 409         Bucket *f;
 410         Bucket *s;
 411         zval *first;
 412         zval *second;
 413 
 414         f = (Bucket *) a;
 415         s = (Bucket *) b;
 416 
 417         first = &f->val;
 418         second = &s->val;
 419 
 420         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
 421                 first = Z_INDIRECT_P(first);
 422         }
 423         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
 424                 second = Z_INDIRECT_P(second);
 425         }
 426 
 427         return numeric_compare_function(first, second);
 428 }
 429 /* }}} */
 430 
 431 static int php_array_reverse_data_compare_numeric(const void *a, const void *b) /* {{{ */
 432 {
 433         return php_array_data_compare_numeric(b, a);
 434 }
 435 /* }}} */
 436 
 437 static int php_array_data_compare_string_case(const void *a, const void *b) /* {{{ */
 438 {
 439         Bucket *f;
 440         Bucket *s;
 441         zval *first;
 442         zval *second;
 443 
 444         f = (Bucket *) a;
 445         s = (Bucket *) b;
 446 
 447         first = &f->val;
 448         second = &s->val;
 449 
 450         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
 451                 first = Z_INDIRECT_P(first);
 452         }
 453         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
 454                 second = Z_INDIRECT_P(second);
 455         }
 456 
 457         return string_case_compare_function(first, second);
 458 }
 459 /* }}} */
 460 
 461 static int php_array_reverse_data_compare_string_case(const void *a, const void *b) /* {{{ */
 462 {
 463         return php_array_data_compare_string_case(b, a);
 464 }
 465 /* }}} */
 466 
 467 static int php_array_data_compare_string(const void *a, const void *b) /* {{{ */
 468 {
 469         Bucket *f;
 470         Bucket *s;
 471         zval *first;
 472         zval *second;
 473 
 474         f = (Bucket *) a;
 475         s = (Bucket *) b;
 476 
 477         first = &f->val;
 478         second = &s->val;
 479 
 480         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
 481                 first = Z_INDIRECT_P(first);
 482         }
 483         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
 484                 second = Z_INDIRECT_P(second);
 485         }
 486 
 487         return string_compare_function(first, second);
 488 }
 489 /* }}} */
 490 
 491 static int php_array_reverse_data_compare_string(const void *a, const void *b) /* {{{ */
 492 {
 493         return php_array_data_compare_string(b, a);
 494 }
 495 /* }}} */
 496 
 497 static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */
 498 {
 499         Bucket *f = (Bucket *) a;
 500         Bucket *s = (Bucket *) b;
 501         zend_string *str1 = zval_get_string(&f->val);
 502         zend_string *str2 = zval_get_string(&s->val);
 503 
 504         int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
 505 
 506         zend_string_release(str1);
 507         zend_string_release(str2);
 508         return result;
 509 }
 510 /* }}} */
 511 
 512 static int php_array_natural_compare(const void *a, const void *b) /* {{{ */
 513 {
 514         return php_array_natural_general_compare(a, b, 0);
 515 }
 516 /* }}} */
 517 
 518 static int php_array_reverse_natural_compare(const void *a, const void *b) /* {{{ */
 519 {
 520         return php_array_natural_general_compare(b, a, 0);
 521 }
 522 /* }}} */
 523 
 524 static int php_array_natural_case_compare(const void *a, const void *b) /* {{{ */
 525 {
 526         return php_array_natural_general_compare(a, b, 1);
 527 }
 528 /* }}} */
 529 
 530 static int php_array_reverse_natural_case_compare(const void *a, const void *b) /* {{{ */
 531 {
 532         return php_array_natural_general_compare(b, a, 1);
 533 }
 534 /* }}} */
 535 
 536 #if HAVE_STRCOLL
 537 static int php_array_data_compare_string_locale(const void *a, const void *b) /* {{{ */
 538 {
 539         Bucket *f;
 540         Bucket *s;
 541         zval *first;
 542         zval *second;
 543 
 544         f = (Bucket *) a;
 545         s = (Bucket *) b;
 546 
 547         first = &f->val;
 548         second = &s->val;
 549 
 550         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
 551                 first = Z_INDIRECT_P(first);
 552         }
 553         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
 554                 second = Z_INDIRECT_P(second);
 555         }
 556 
 557         return string_locale_compare_function(first, second);
 558 }
 559 /* }}} */
 560 
 561 static int php_array_reverse_data_compare_string_locale(const void *a, const void *b) /* {{{ */
 562 {
 563         return php_array_data_compare_string_locale(b, a);
 564 }
 565 /* }}} */
 566 #endif
 567 
 568 static compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */
 569 {
 570         switch (sort_type & ~PHP_SORT_FLAG_CASE) {
 571                 case PHP_SORT_NUMERIC:
 572                         if (reverse) {
 573                                 return php_array_reverse_key_compare_numeric;
 574                         } else {
 575                                 return php_array_key_compare_numeric;
 576                         }
 577                         break;
 578 
 579                 case PHP_SORT_STRING:
 580                         if (sort_type & PHP_SORT_FLAG_CASE) {
 581                                 if (reverse) {
 582                                         return php_array_reverse_key_compare_string_case;
 583                                 } else {
 584                                         return php_array_key_compare_string_case;
 585                                 }
 586                         } else {
 587                                 if (reverse) {
 588                                         return php_array_reverse_key_compare_string;
 589                                 } else {
 590                                         return php_array_key_compare_string;
 591                                 }
 592                         }
 593                         break;
 594 
 595                 case PHP_SORT_NATURAL:
 596                         if (sort_type & PHP_SORT_FLAG_CASE) {
 597                                 if (reverse) {
 598                                         return php_array_reverse_key_compare_string_natural_case;
 599                                 } else {
 600                                         return php_array_key_compare_string_natural_case;
 601                                 }
 602                         } else {
 603                                 if (reverse) {
 604                                         return php_array_reverse_key_compare_string_natural;
 605                                 } else {
 606                                         return php_array_key_compare_string_natural;
 607                                 }
 608                         }
 609                         break;
 610 
 611 #if HAVE_STRCOLL
 612                 case PHP_SORT_LOCALE_STRING:
 613                         if (reverse) {
 614                                 return php_array_reverse_key_compare_string_locale;
 615                         } else {
 616                                 return php_array_key_compare_string_locale;
 617                         }
 618                         break;
 619 #endif
 620 
 621                 case PHP_SORT_REGULAR:
 622                 default:
 623                         if (reverse) {
 624                                 return php_array_reverse_key_compare;
 625                         } else {
 626                                 return php_array_key_compare;
 627                         }
 628                         break;
 629         }
 630         return NULL;
 631 }
 632 /* }}} */
 633 
 634 static compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */
 635 {
 636         switch (sort_type & ~PHP_SORT_FLAG_CASE) {
 637                 case PHP_SORT_NUMERIC:
 638                         if (reverse) {
 639                                 return php_array_reverse_data_compare_numeric;
 640                         } else {
 641                                 return php_array_data_compare_numeric;
 642                         }
 643                         break;
 644 
 645                 case PHP_SORT_STRING:
 646                         if (sort_type & PHP_SORT_FLAG_CASE) {
 647                                 if (reverse) {
 648                                         return php_array_reverse_data_compare_string_case;
 649                                 } else {
 650                                         return php_array_data_compare_string_case;
 651                                 }
 652                         } else {
 653                                 if (reverse) {
 654                                         return php_array_reverse_data_compare_string;
 655                                 } else {
 656                                         return php_array_data_compare_string;
 657                                 }
 658                         }
 659                         break;
 660 
 661                 case PHP_SORT_NATURAL:
 662                         if (sort_type & PHP_SORT_FLAG_CASE) {
 663                                 if (reverse) {
 664                                         return php_array_reverse_natural_case_compare;
 665                                 } else {
 666                                         return php_array_natural_case_compare;
 667                                 }
 668                         } else {
 669                                 if (reverse) {
 670                                         return php_array_reverse_natural_compare;
 671                                 } else {
 672                                         return php_array_natural_compare;
 673                                 }
 674                         }
 675                         break;
 676 
 677 #if HAVE_STRCOLL
 678                 case PHP_SORT_LOCALE_STRING:
 679                         if (reverse) {
 680                                 return php_array_reverse_data_compare_string_locale;
 681                         } else {
 682                                 return php_array_data_compare_string_locale;
 683                         }
 684                         break;
 685 #endif
 686 
 687                 case PHP_SORT_REGULAR:
 688                 default:
 689                         if (reverse) {
 690                                 return php_array_reverse_data_compare;
 691                         } else {
 692                                 return php_array_data_compare;
 693                         }
 694                         break;
 695         }
 696         return NULL;
 697 }
 698 /* }}} */
 699 
 700 /* {{{ proto bool krsort(array &array_arg [, int sort_flags])
 701    Sort an array by key value in reverse order */
 702 PHP_FUNCTION(krsort)
 703 {
 704         zval *array;
 705         zend_long sort_type = PHP_SORT_REGULAR;
 706         compare_func_t cmp;
 707 
 708 #ifndef FAST_ZPP
 709         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 710                 RETURN_FALSE;
 711         }
 712 #else
 713         ZEND_PARSE_PARAMETERS_START(1, 2)
 714                 Z_PARAM_ARRAY_EX(array, 0, 1)
 715                 Z_PARAM_OPTIONAL
 716                 Z_PARAM_LONG(sort_type)
 717         ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
 718 #endif
 719 
 720         cmp = php_get_key_compare_func(sort_type, 1);
 721 
 722         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
 723                 RETURN_FALSE;
 724         }
 725         RETURN_TRUE;
 726 }
 727 /* }}} */
 728 
 729 /* {{{ proto bool ksort(array &array_arg [, int sort_flags])
 730    Sort an array by key */
 731 PHP_FUNCTION(ksort)
 732 {
 733         zval *array;
 734         zend_long sort_type = PHP_SORT_REGULAR;
 735         compare_func_t cmp;
 736 
 737 #ifndef FAST_ZPP
 738         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 739                 RETURN_FALSE;
 740         }
 741 #else
 742         ZEND_PARSE_PARAMETERS_START(1, 2)
 743                 Z_PARAM_ARRAY_EX(array, 0, 1)
 744                 Z_PARAM_OPTIONAL
 745                 Z_PARAM_LONG(sort_type)
 746         ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
 747 #endif
 748 
 749         cmp = php_get_key_compare_func(sort_type, 0);
 750 
 751         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
 752                 RETURN_FALSE;
 753         }
 754         RETURN_TRUE;
 755 }
 756 /* }}} */
 757 
 758 PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */
 759 {
 760         zend_long cnt = 0;
 761         zval *element;
 762 
 763         if (Z_TYPE_P(array) == IS_ARRAY) {
 764                 if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) {
 765                         php_error_docref(NULL, E_WARNING, "recursion detected");
 766                         return 0;
 767                 }
 768 
 769                 cnt = zend_array_count(Z_ARRVAL_P(array));
 770                 if (mode == COUNT_RECURSIVE) {
 771                     if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
 772                                 Z_ARRVAL_P(array)->u.v.nApplyCount++;
 773                         }
 774                         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
 775                                 ZVAL_DEREF(element);
 776                                 cnt += php_count_recursive(element, COUNT_RECURSIVE);
 777                         } ZEND_HASH_FOREACH_END();
 778                     if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
 779                                 Z_ARRVAL_P(array)->u.v.nApplyCount--;
 780                         }
 781                 }
 782         }
 783 
 784         return cnt;
 785 }
 786 /* }}} */
 787 
 788 /* {{{ proto int count(mixed var [, int mode])
 789    Count the number of elements in a variable (usually an array) */
 790 PHP_FUNCTION(count)
 791 {
 792         zval *array;
 793         zend_long mode = COUNT_NORMAL;
 794         zend_long cnt;
 795         zval *element;
 796 
 797 #ifndef FAST_ZPP
 798         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &array, &mode) == FAILURE) {
 799                 return;
 800         }
 801 #else
 802         ZEND_PARSE_PARAMETERS_START(1, 2)
 803                 Z_PARAM_ZVAL(array)
 804                 Z_PARAM_OPTIONAL
 805                 Z_PARAM_LONG(mode)
 806         ZEND_PARSE_PARAMETERS_END();
 807 #endif
 808 
 809         switch (Z_TYPE_P(array)) {
 810                 case IS_NULL:
 811                         RETURN_LONG(0);
 812                         break;
 813                 case IS_ARRAY:
 814                         cnt = zend_array_count(Z_ARRVAL_P(array));
 815                         if (mode == COUNT_RECURSIVE) {
 816                                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
 817                                         ZVAL_DEREF(element);
 818                                         cnt += php_count_recursive(element, COUNT_RECURSIVE);
 819                                 } ZEND_HASH_FOREACH_END();
 820                         }
 821                         RETURN_LONG(cnt);
 822                         break;
 823                 case IS_OBJECT: {
 824 #ifdef HAVE_SPL
 825                         zval retval;
 826 #endif
 827                         /* first, we check if the handler is defined */
 828                         if (Z_OBJ_HT_P(array)->count_elements) {
 829                                 RETVAL_LONG(1);
 830                                 if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value))) {
 831                                         return;
 832                                 }
 833                         }
 834 #ifdef HAVE_SPL
 835                         /* if not and the object implements Countable we call its count() method */
 836                         if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) {
 837                                 zend_call_method_with_0_params(array, NULL, NULL, "count", &retval);
 838                                 if (Z_TYPE(retval) != IS_UNDEF) {
 839                                         RETVAL_LONG(zval_get_long(&retval));
 840                                         zval_ptr_dtor(&retval);
 841                                 }
 842                                 return;
 843                         }
 844 #endif
 845                 }
 846                 default:
 847                         RETURN_LONG(1);
 848                         break;
 849         }
 850 }
 851 /* }}} */
 852 
 853 static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
 854 {
 855         zval *array;
 856 
 857         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) {
 858                 return;
 859         }
 860 
 861         if (fold_case) {
 862                 if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0) == FAILURE) {
 863                         return;
 864                 }
 865         } else {
 866                 if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0) == FAILURE) {
 867                         return;
 868                 }
 869         }
 870 
 871         RETURN_TRUE;
 872 }
 873 /* }}} */
 874 
 875 /* {{{ proto void natsort(array &array_arg)
 876    Sort an array using natural sort */
 877 PHP_FUNCTION(natsort)
 878 {
 879         php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
 880 }
 881 /* }}} */
 882 
 883 /* {{{ proto void natcasesort(array &array_arg)
 884    Sort an array using case-insensitive natural sort */
 885 PHP_FUNCTION(natcasesort)
 886 {
 887         php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 888 }
 889 /* }}} */
 890 
 891 /* {{{ proto bool asort(array &array_arg [, int sort_flags])
 892    Sort an array and maintain index association */
 893 PHP_FUNCTION(asort)
 894 {
 895         zval *array;
 896         zend_long sort_type = PHP_SORT_REGULAR;
 897         compare_func_t cmp;
 898 
 899         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 900                 RETURN_FALSE;
 901         }
 902 
 903         cmp = php_get_data_compare_func(sort_type, 0);
 904 
 905         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
 906                 RETURN_FALSE;
 907         }
 908         RETURN_TRUE;
 909 }
 910 /* }}} */
 911 
 912 /* {{{ proto bool arsort(array &array_arg [, int sort_flags])
 913    Sort an array in reverse order and maintain index association */
 914 PHP_FUNCTION(arsort)
 915 {
 916         zval *array;
 917         zend_long sort_type = PHP_SORT_REGULAR;
 918         compare_func_t cmp;
 919 
 920         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 921                 RETURN_FALSE;
 922         }
 923 
 924         cmp = php_get_data_compare_func(sort_type, 1);
 925 
 926         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
 927                 RETURN_FALSE;
 928         }
 929         RETURN_TRUE;
 930 }
 931 /* }}} */
 932 
 933 /* {{{ proto bool sort(array &array_arg [, int sort_flags])
 934    Sort an array */
 935 PHP_FUNCTION(sort)
 936 {
 937         zval *array;
 938         zend_long sort_type = PHP_SORT_REGULAR;
 939         compare_func_t cmp;
 940 
 941         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 942                 RETURN_FALSE;
 943         }
 944 
 945         cmp = php_get_data_compare_func(sort_type, 0);
 946 
 947         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
 948                 RETURN_FALSE;
 949         }
 950         RETURN_TRUE;
 951 }
 952 /* }}} */
 953 
 954 /* {{{ proto bool rsort(array &array_arg [, int sort_flags])
 955    Sort an array in reverse order */
 956 PHP_FUNCTION(rsort)
 957 {
 958         zval *array;
 959         zend_long sort_type = PHP_SORT_REGULAR;
 960         compare_func_t cmp;
 961 
 962         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
 963                 RETURN_FALSE;
 964         }
 965 
 966         cmp = php_get_data_compare_func(sort_type, 1);
 967 
 968         if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
 969                 RETURN_FALSE;
 970         }
 971         RETURN_TRUE;
 972 }
 973 /* }}} */
 974 
 975 static int php_array_user_compare(const void *a, const void *b) /* {{{ */
 976 {
 977         Bucket *f;
 978         Bucket *s;
 979         zval args[2];
 980         zval retval;
 981 
 982         f = (Bucket *) a;
 983         s = (Bucket *) b;
 984 
 985         ZVAL_COPY(&args[0], &f->val);
 986         ZVAL_COPY(&args[1], &s->val);
 987 
 988         BG(user_compare_fci).param_count = 2;
 989         BG(user_compare_fci).params = args;
 990         BG(user_compare_fci).retval = &retval;
 991         BG(user_compare_fci).no_separation = 0;
 992         if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
 993                 zend_long ret = zval_get_long(&retval);
 994                 zval_ptr_dtor(&retval);
 995                 zval_ptr_dtor(&args[1]);
 996                 zval_ptr_dtor(&args[0]);
 997                 return ret < 0 ? -1 : ret > 0 ? 1 : 0;
 998         } else {
 999                 zval_ptr_dtor(&args[1]);
1000                 zval_ptr_dtor(&args[0]);
1001                 return 0;
1002         }
1003 }
1004 /* }}} */
1005 
1006 /* check if comparison function is valid */
1007 #define PHP_ARRAY_CMP_FUNC_CHECK(func_name)     \
1008         if (!zend_is_callable(*func_name, 0, NULL)) {   \
1009                 php_error_docref(NULL, E_WARNING, "Invalid comparison function");       \
1010                 BG(user_compare_fci) = old_user_compare_fci; \
1011                 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
1012                 RETURN_FALSE;   \
1013         }       \
1014 
1015         /* Clear FCI cache otherwise : for example the same or other array with
1016          * (partly) the same key values has been sorted with uasort() or
1017          * other sorting function the comparison is cached, however the name
1018          * of the function for comparison is not respected. see bug #28739 AND #33295
1019          *
1020          * Following defines will assist in backup / restore values. */
1021 
1022 #define PHP_ARRAY_CMP_FUNC_VARS \
1023         zend_fcall_info old_user_compare_fci; \
1024         zend_fcall_info_cache old_user_compare_fci_cache \
1025 
1026 #define PHP_ARRAY_CMP_FUNC_BACKUP() \
1027         old_user_compare_fci = BG(user_compare_fci); \
1028         old_user_compare_fci_cache = BG(user_compare_fci_cache); \
1029         BG(user_compare_fci_cache) = empty_fcall_info_cache; \
1030 
1031 #define PHP_ARRAY_CMP_FUNC_RESTORE() \
1032         BG(user_compare_fci) = old_user_compare_fci; \
1033         BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
1034 
1035 static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */
1036 {
1037         zval *array;
1038         zend_refcounted *arr;
1039         zend_bool retval;
1040         PHP_ARRAY_CMP_FUNC_VARS;
1041 
1042         PHP_ARRAY_CMP_FUNC_BACKUP();
1043 
1044         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
1045                 PHP_ARRAY_CMP_FUNC_RESTORE();
1046                 return;
1047         }
1048 
1049         /* Increase reference counter, so the attempts to modify the array in user
1050          * comparison function will create a copy of array and won't affect the
1051          * original array. The fact of modification is detected by comparing the
1052          * zend_array pointer. The result of sorting in such case is undefined and
1053          * the function returns FALSE.
1054          */
1055         Z_ADDREF_P(array);
1056         arr = Z_COUNTED_P(array);
1057 
1058         retval = zend_hash_sort(Z_ARRVAL_P(array), compare_func, renumber) != FAILURE;
1059 
1060         if (arr != Z_COUNTED_P(array)) {
1061                 php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function");
1062                 if (--GC_REFCOUNT(arr) <= 0) {
1063                         _zval_dtor_func(arr ZEND_FILE_LINE_CC);
1064                 }
1065                 retval = 0;
1066         } else {
1067                 Z_DELREF_P(array);
1068         }
1069 
1070         PHP_ARRAY_CMP_FUNC_RESTORE();
1071         RETURN_BOOL(retval);
1072 }
1073 /* }}} */
1074 
1075 /* {{{ proto bool usort(array array_arg, string cmp_function)
1076    Sort an array by values using a user-defined comparison function */
1077 PHP_FUNCTION(usort)
1078 {
1079         php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
1080 }
1081 /* }}} */
1082 
1083 /* {{{ proto bool uasort(array array_arg, string cmp_function)
1084    Sort an array with a user-defined comparison function and maintain index association */
1085 PHP_FUNCTION(uasort)
1086 {
1087         php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
1088 }
1089 /* }}} */
1090 
1091 static int php_array_user_key_compare(const void *a, const void *b) /* {{{ */
1092 {
1093         Bucket *f;
1094         Bucket *s;
1095         zval args[2];
1096         zval retval;
1097         zend_long result;
1098 
1099         ZVAL_NULL(&args[0]);
1100         ZVAL_NULL(&args[1]);
1101 
1102         f = (Bucket *) a;
1103         s = (Bucket *) b;
1104 
1105         if (f->key == NULL) {
1106                 ZVAL_LONG(&args[0], f->h);
1107         } else {
1108                 ZVAL_STR_COPY(&args[0], f->key);
1109         }
1110         if (s->key == NULL) {
1111                 ZVAL_LONG(&args[1], s->h);
1112         } else {
1113                 ZVAL_STR_COPY(&args[1], s->key);
1114         }
1115 
1116         BG(user_compare_fci).param_count = 2;
1117         BG(user_compare_fci).params = args;
1118         BG(user_compare_fci).retval = &retval;
1119         BG(user_compare_fci).no_separation = 0;
1120         if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1121                 result = zval_get_long(&retval);
1122                 zval_ptr_dtor(&retval);
1123         } else {
1124                 result = 0;
1125         }
1126 
1127         zval_ptr_dtor(&args[0]);
1128         zval_ptr_dtor(&args[1]);
1129 
1130         return result < 0 ? -1 : result > 0 ? 1 : 0;
1131 }
1132 /* }}} */
1133 
1134 /* {{{ proto bool uksort(array array_arg, string cmp_function)
1135    Sort an array by keys using a user-defined comparison function */
1136 PHP_FUNCTION(uksort)
1137 {
1138         php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
1139 }
1140 /* }}} */
1141 
1142 /* {{{ proto mixed end(array array_arg)
1143    Advances array argument's internal pointer to the last element and return it */
1144 PHP_FUNCTION(end)
1145 {
1146         HashTable *array;
1147         zval *entry;
1148 
1149 #ifndef FAST_ZPP
1150         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) {
1151                 return;
1152         }
1153 #else
1154         ZEND_PARSE_PARAMETERS_START(1, 1)
1155                 Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1156         ZEND_PARSE_PARAMETERS_END();
1157 #endif
1158 
1159         zend_hash_internal_pointer_end(array);
1160 
1161         if (USED_RET()) {
1162                 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1163                         RETURN_FALSE;
1164                 }
1165 
1166                 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1167                         entry = Z_INDIRECT_P(entry);
1168                 }
1169 
1170                 ZVAL_DEREF(entry);
1171                 ZVAL_COPY(return_value, entry);
1172         }
1173 }
1174 /* }}} */
1175 
1176 /* {{{ proto mixed prev(array array_arg)
1177    Move array argument's internal pointer to the previous element and return it */
1178 PHP_FUNCTION(prev)
1179 {
1180         HashTable *array;
1181         zval *entry;
1182 
1183 #ifndef FAST_ZPP
1184         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) {
1185                 return;
1186         }
1187 #else
1188         ZEND_PARSE_PARAMETERS_START(1, 1)
1189                 Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1190         ZEND_PARSE_PARAMETERS_END();
1191 #endif
1192 
1193         zend_hash_move_backwards(array);
1194 
1195         if (USED_RET()) {
1196                 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1197                         RETURN_FALSE;
1198                 }
1199 
1200                 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1201                         entry = Z_INDIRECT_P(entry);
1202                 }
1203 
1204                 ZVAL_DEREF(entry);
1205                 ZVAL_COPY(return_value, entry);
1206         }
1207 }
1208 /* }}} */
1209 
1210 /* {{{ proto mixed next(array array_arg)
1211    Move array argument's internal pointer to the next element and return it */
1212 PHP_FUNCTION(next)
1213 {
1214         HashTable *array;
1215         zval *entry;
1216 
1217 #ifndef FAST_ZPP
1218         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) {
1219                 return;
1220         }
1221 #else
1222         ZEND_PARSE_PARAMETERS_START(1, 1)
1223                 Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1224         ZEND_PARSE_PARAMETERS_END();
1225 #endif
1226 
1227         zend_hash_move_forward(array);
1228 
1229         if (USED_RET()) {
1230                 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1231                         RETURN_FALSE;
1232                 }
1233 
1234                 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1235                         entry = Z_INDIRECT_P(entry);
1236                 }
1237 
1238                 ZVAL_DEREF(entry);
1239                 ZVAL_COPY(return_value, entry);
1240         }
1241 }
1242 /* }}} */
1243 
1244 /* {{{ proto mixed reset(array array_arg)
1245    Set array argument's internal pointer to the first element and return it */
1246 PHP_FUNCTION(reset)
1247 {
1248         HashTable *array;
1249         zval *entry;
1250 
1251 #ifndef FAST_ZPP
1252         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) {
1253                 return;
1254         }
1255 #else
1256         ZEND_PARSE_PARAMETERS_START(1, 1)
1257                 Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1258         ZEND_PARSE_PARAMETERS_END();
1259 #endif
1260 
1261         zend_hash_internal_pointer_reset(array);
1262 
1263         if (USED_RET()) {
1264                 if ((entry = zend_hash_get_current_data(array)) == NULL) {
1265                         RETURN_FALSE;
1266                 }
1267 
1268                 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1269                         entry = Z_INDIRECT_P(entry);
1270                 }
1271 
1272                 ZVAL_DEREF(entry);
1273                 ZVAL_COPY(return_value, entry);
1274         }
1275 }
1276 /* }}} */
1277 
1278 /* {{{ proto mixed current(array array_arg)
1279    Return the element currently pointed to by the internal array pointer */
1280 PHP_FUNCTION(current)
1281 {
1282         HashTable *array;
1283         zval *entry;
1284 
1285 #ifndef FAST_ZPP
1286         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H", &array) == FAILURE) {
1287                 return;
1288         }
1289 #else
1290         ZEND_PARSE_PARAMETERS_START(1, 1)
1291                 Z_PARAM_ARRAY_OR_OBJECT_HT(array)
1292         ZEND_PARSE_PARAMETERS_END();
1293 #endif
1294 
1295         if ((entry = zend_hash_get_current_data(array)) == NULL) {
1296                 RETURN_FALSE;
1297         }
1298 
1299         if (Z_TYPE_P(entry) == IS_INDIRECT) {
1300                 entry = Z_INDIRECT_P(entry);
1301         }
1302 
1303         ZVAL_DEREF(entry);
1304         ZVAL_COPY(return_value, entry);
1305 }
1306 /* }}} */
1307 
1308 /* {{{ proto mixed key(array array_arg)
1309    Return the key of the element currently pointed to by the internal array pointer */
1310 PHP_FUNCTION(key)
1311 {
1312         HashTable *array;
1313 
1314 #ifndef FAST_ZPP
1315         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H", &array) == FAILURE) {
1316                 return;
1317         }
1318 #else
1319         ZEND_PARSE_PARAMETERS_START(1, 1)
1320                 Z_PARAM_ARRAY_OR_OBJECT_HT(array)
1321         ZEND_PARSE_PARAMETERS_END();
1322 #endif
1323 
1324         zend_hash_get_current_key_zval(array, return_value);
1325 }
1326 /* }}} */
1327 
1328 /* {{{ proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1329    Return the lowest value in an array or a series of arguments */
1330 PHP_FUNCTION(min)
1331 {
1332         int argc;
1333         zval *args = NULL;
1334 
1335         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
1336                 return;
1337         }
1338 
1339         /* mixed min ( array $values ) */
1340         if (argc == 1) {
1341                 zval *result;
1342 
1343                 if (Z_TYPE(args[0]) != IS_ARRAY) {
1344                         php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array");
1345                         RETVAL_NULL();
1346                 } else {
1347                         if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) {
1348                                 ZVAL_DEREF(result);
1349                                 ZVAL_COPY(return_value, result);
1350                         } else {
1351                                 php_error_docref(NULL, E_WARNING, "Array must contain at least one element");
1352                                 RETVAL_FALSE;
1353                         }
1354                 }
1355         } else {
1356                 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1357                 zval *min, result;
1358                 int i;
1359 
1360                 min = &args[0];
1361 
1362                 for (i = 1; i < argc; i++) {
1363                         is_smaller_function(&result, &args[i], min);
1364                         if (Z_TYPE(result) == IS_TRUE) {
1365                                 min = &args[i];
1366                         }
1367                 }
1368 
1369                 ZVAL_DEREF(min);
1370                 ZVAL_COPY(return_value, min);
1371         }
1372 }
1373 /* }}} */
1374 
1375 /* {{{ proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1376    Return the highest value in an array or a series of arguments */
1377 PHP_FUNCTION(max)
1378 {
1379         zval *args = NULL;
1380         int argc;
1381 
1382         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
1383                 return;
1384         }
1385 
1386         /* mixed max ( array $values ) */
1387         if (argc == 1) {
1388                 zval *result;
1389 
1390                 if (Z_TYPE(args[0]) != IS_ARRAY) {
1391                         php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array");
1392                         RETVAL_NULL();
1393                 } else {
1394                         if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) {
1395                                 ZVAL_DEREF(result);
1396                                 ZVAL_COPY(return_value, result);
1397                         } else {
1398                                 php_error_docref(NULL, E_WARNING, "Array must contain at least one element");
1399                                 RETVAL_FALSE;
1400                         }
1401                 }
1402         } else {
1403                 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1404                 zval *max, result;
1405                 int i;
1406 
1407                 max = &args[0];
1408 
1409                 for (i = 1; i < argc; i++) {
1410                         is_smaller_or_equal_function(&result, &args[i], max);
1411                         if (Z_TYPE(result) == IS_FALSE) {
1412                                 max = &args[i];
1413                         }
1414                 }
1415 
1416                 ZVAL_DEREF(max);
1417                 ZVAL_COPY(return_value, max);
1418         }
1419 }
1420 /* }}} */
1421 
1422 static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive) /* {{{ */
1423 {
1424         zval args[3],           /* Arguments to userland function */
1425                  retval,                /* Return value - unused */
1426                  *zv;
1427 
1428         /* Set up known arguments */
1429         ZVAL_UNDEF(&args[1]);
1430         if (userdata) {
1431                 ZVAL_COPY(&args[2], userdata);
1432         }
1433 
1434         BG(array_walk_fci).retval = &retval;
1435         BG(array_walk_fci).param_count = userdata ? 3 : 2;
1436         BG(array_walk_fci).params = args;
1437         BG(array_walk_fci).no_separation = 0;
1438 
1439         /* Iterate through hash */
1440         zend_hash_internal_pointer_reset(target_hash);
1441         while (!EG(exception) && (zv = zend_hash_get_current_data(target_hash)) != NULL) {
1442                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
1443                         zv = Z_INDIRECT_P(zv);
1444                         if (Z_TYPE_P(zv) == IS_UNDEF) {
1445                                 zend_hash_move_forward(target_hash);
1446                                 continue;
1447                         }
1448                 }
1449                 if (recursive &&
1450                     (Z_TYPE_P(zv) == IS_ARRAY ||
1451                      (Z_ISREF_P(zv) && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY))) {
1452                         HashTable *thash;
1453                         zend_fcall_info orig_array_walk_fci;
1454                         zend_fcall_info_cache orig_array_walk_fci_cache;
1455 
1456                         ZVAL_DEREF(zv);
1457                         SEPARATE_ARRAY(zv);
1458                         thash = Z_ARRVAL_P(zv);
1459                         if (thash->u.v.nApplyCount > 1) {
1460                                 php_error_docref(NULL, E_WARNING, "recursion detected");
1461                                 if (userdata) {
1462                                         zval_ptr_dtor(&args[2]);
1463                                 }
1464                                 return 0;
1465                         }
1466 
1467                         /* backup the fcall info and cache */
1468                         orig_array_walk_fci = BG(array_walk_fci);
1469                         orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1470 
1471                         thash->u.v.nApplyCount++;
1472                         php_array_walk(thash, userdata, recursive);
1473                         thash->u.v.nApplyCount--;
1474 
1475                         /* restore the fcall info and cache */
1476                         BG(array_walk_fci) = orig_array_walk_fci;
1477                         BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1478                 } else {
1479                         int was_ref = Z_ISREF_P(zv);
1480 
1481                         ZVAL_COPY(&args[0], zv);
1482 
1483                         /* Allocate space for key */
1484                         zend_hash_get_current_key_zval(target_hash, &args[1]);
1485 
1486                         /* Call the userland function */
1487                         if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache)) == SUCCESS) {
1488                                 if (!was_ref && Z_ISREF(args[0])) {
1489                                         /* copy reference back */
1490                                         zval garbage;
1491 
1492                                         ZVAL_COPY_VALUE(&garbage, zv);
1493                                         ZVAL_COPY_VALUE(zv, &args[0]);
1494                                         zval_ptr_dtor(&garbage);
1495                                 } else {
1496                                         zval_ptr_dtor(&args[0]);
1497                                 }
1498                                 zval_ptr_dtor(&retval);
1499                         } else {
1500                                 zval_ptr_dtor(&args[0]);
1501                                 if (Z_TYPE(args[1]) != IS_UNDEF) {
1502                                         zval_ptr_dtor(&args[1]);
1503                                         ZVAL_UNDEF(&args[1]);
1504                                 }
1505                                 break;
1506                         }
1507                 }
1508 
1509                 if (Z_TYPE(args[1]) != IS_UNDEF) {
1510                         zval_ptr_dtor(&args[1]);
1511                         ZVAL_UNDEF(&args[1]);
1512                 }
1513                 zend_hash_move_forward(target_hash);
1514         }
1515 
1516         if (userdata) {
1517                 zval_ptr_dtor(&args[2]);
1518         }
1519         return 0;
1520 }
1521 /* }}} */
1522 
1523 /* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
1524    Apply a user function to every member of an array */
1525 PHP_FUNCTION(array_walk)
1526 {
1527         HashTable *array;
1528         zval *userdata = NULL;
1529         zend_fcall_info orig_array_walk_fci;
1530         zend_fcall_info_cache orig_array_walk_fci_cache;
1531 
1532         orig_array_walk_fci = BG(array_walk_fci);
1533         orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1534 
1535 #ifndef FAST_ZPP
1536         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1537                 BG(array_walk_fci) = orig_array_walk_fci;
1538                 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1539                 return;
1540         }
1541 #else
1542         ZEND_PARSE_PARAMETERS_START(2, 3)
1543                 Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
1544                 Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
1545                 Z_PARAM_OPTIONAL
1546                 Z_PARAM_ZVAL_EX(userdata, 0, 1)
1547         ZEND_PARSE_PARAMETERS_END_EX(
1548                 BG(array_walk_fci) = orig_array_walk_fci;
1549                 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1550                 return
1551         );
1552 #endif
1553 
1554         php_array_walk(array, userdata, 0);
1555         BG(array_walk_fci) = orig_array_walk_fci;
1556         BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1557         RETURN_TRUE;
1558 }
1559 /* }}} */
1560 
1561 /* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
1562    Apply a user function recursively to every member of an array */
1563 PHP_FUNCTION(array_walk_recursive)
1564 {
1565         HashTable *array;
1566         zval *userdata = NULL;
1567         zend_fcall_info orig_array_walk_fci;
1568         zend_fcall_info_cache orig_array_walk_fci_cache;
1569 
1570         orig_array_walk_fci = BG(array_walk_fci);
1571         orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1572 
1573         if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1574                 BG(array_walk_fci) = orig_array_walk_fci;
1575                 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1576                 return;
1577         }
1578 
1579         php_array_walk(array, userdata, 1);
1580         BG(array_walk_fci) = orig_array_walk_fci;
1581         BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1582         RETURN_TRUE;
1583 }
1584 /* }}} */
1585 
1586 /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1587  * 0 = return boolean
1588  * 1 = return key
1589  */
1590 static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1591 {
1592         zval *value,                            /* value to check for */
1593                  *array,                                /* array to check in */
1594                  *entry;                                /* pointer to array entry */
1595         zend_ulong num_idx;
1596         zend_string *str_idx;
1597         zend_bool strict = 0;           /* strict comparison or not */
1598 
1599 #ifndef FAST_ZPP
1600         if (zend_parse_parameters(ZEND_NUM_ARGS(), "za|b", &value, &array, &strict) == FAILURE) {
1601                 return;
1602         }
1603 #else
1604         ZEND_PARSE_PARAMETERS_START(2, 3)
1605                 Z_PARAM_ZVAL(value)
1606                 Z_PARAM_ARRAY(array)
1607                 Z_PARAM_OPTIONAL
1608                 Z_PARAM_BOOL(strict)
1609         ZEND_PARSE_PARAMETERS_END();
1610 #endif
1611 
1612         if (strict) {
1613                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1614                         ZVAL_DEREF(entry);
1615                         if (fast_is_identical_function(value, entry)) {
1616                                 if (behavior == 0) {
1617                                         RETURN_TRUE;
1618                                 } else {
1619                                         if (str_idx) {
1620                                                 RETVAL_STR_COPY(str_idx);
1621                                         } else {
1622                                                 RETVAL_LONG(num_idx);
1623                                         }
1624                                         return;
1625                                 }
1626                         }
1627                 } ZEND_HASH_FOREACH_END();
1628         } else {
1629                 ZVAL_DEREF(value);
1630                 if (Z_TYPE_P(value) == IS_LONG) {
1631                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1632                                 if (fast_equal_check_long(value, entry)) {
1633                                         if (behavior == 0) {
1634                                                 RETURN_TRUE;
1635                                         } else {
1636                                                 if (str_idx) {
1637                                                         RETVAL_STR_COPY(str_idx);
1638                                                 } else {
1639                                                         RETVAL_LONG(num_idx);
1640                                                 }
1641                                                 return;
1642                                         }
1643                                 }
1644                         } ZEND_HASH_FOREACH_END();
1645                 } else if (Z_TYPE_P(value) == IS_STRING) {
1646                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1647                                 if (fast_equal_check_string(value, entry)) {
1648                                         if (behavior == 0) {
1649                                                 RETURN_TRUE;
1650                                         } else {
1651                                                 if (str_idx) {
1652                                                         RETVAL_STR_COPY(str_idx);
1653                                                 } else {
1654                                                         RETVAL_LONG(num_idx);
1655                                                 }
1656                                                 return;
1657                                         }
1658                                 }
1659                         } ZEND_HASH_FOREACH_END();
1660                 } else {
1661                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1662                                 if (fast_equal_check_function(value, entry)) {
1663                                         if (behavior == 0) {
1664                                                 RETURN_TRUE;
1665                                         } else {
1666                                                 if (str_idx) {
1667                                                         RETVAL_STR_COPY(str_idx);
1668                                                 } else {
1669                                                         RETVAL_LONG(num_idx);
1670                                                 }
1671                                                 return;
1672                                         }
1673                                 }
1674                         } ZEND_HASH_FOREACH_END();
1675                 }
1676         }
1677 
1678         RETURN_FALSE;
1679 }
1680 /* }}} */
1681 
1682 /* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
1683    Checks if the given value exists in the array */
1684 PHP_FUNCTION(in_array)
1685 {
1686         php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1687 }
1688 /* }}} */
1689 
1690 /* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
1691    Searches the array for a given value and returns the corresponding key if successful */
1692 PHP_FUNCTION(array_search)
1693 {
1694         php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1695 }
1696 /* }}} */
1697 
1698 static zend_always_inline int php_valid_var_name(char *var_name, size_t var_name_len) /* {{{ */
1699 {
1700 #if 1
1701         /* first 256 bits for first character, and second 256 bits for the next */
1702         static const uint32_t charset[16] = {
1703              /*  31      0   63     32   95     64   127    96 */
1704                         0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1705                         0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
1706              /*  31      0   63     32   95     64   127    96 */
1707                         0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1708                         0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
1709                 };
1710 #endif
1711         size_t i;
1712         uint32_t ch;
1713 
1714         if (UNEXPECTED(!var_name_len)) {
1715                 return 0;
1716         }
1717 
1718         /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1719         ch = (uint32_t)((unsigned char *)var_name)[0];
1720 #if 1
1721         if (UNEXPECTED(!(charset[ch >> 5] & (1 << (ch & 0x1f))))) {
1722 #else
1723         if (var_name[0] != '_' &&
1724                 (ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1725                 (ch < 97  /* a    */ || /* z    */ ch > 122) &&
1726                 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1727         ) {
1728 #endif
1729                 return 0;
1730         }
1731 
1732         /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1733         if (var_name_len > 1) {
1734                 i = 1;
1735                 do {
1736                         ch = (uint32_t)((unsigned char *)var_name)[i];
1737 #if 1
1738                         if (UNEXPECTED(!(charset[8 + (ch >> 5)] & (1 << (ch & 0x1f))))) {
1739 #else
1740                         if (var_name[i] != '_' &&
1741                                 (ch < 48  /* 0    */ || /* 9    */ ch > 57)  &&
1742                                 (ch < 65  /* A    */ || /* Z    */ ch > 90)  &&
1743                                 (ch < 97  /* a    */ || /* z    */ ch > 122) &&
1744                                 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1745                         ) {
1746 #endif
1747                                 return 0;
1748                         }
1749                 } while (++i < var_name_len);
1750         }
1751         return 1;
1752 }
1753 /* }}} */
1754 
1755 PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t var_name_len, zend_bool add_underscore) /* {{{ */
1756 {
1757         ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
1758         memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
1759 
1760         if (add_underscore) {
1761                 Z_STRVAL_P(result)[Z_STRLEN_P(prefix)] = '_';
1762         }
1763 
1764         memcpy(Z_STRVAL_P(result) + Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1765 
1766         return SUCCESS;
1767 }
1768 /* }}} */
1769 
1770 /* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
1771    Imports variables into symbol table from an array */
1772 PHP_FUNCTION(extract)
1773 {
1774         zval *var_array_param, *prefix = NULL;
1775         zend_long extract_type = EXTR_OVERWRITE;
1776         zval *entry;
1777         zend_string *var_name;
1778         zend_ulong num_key;
1779         int var_exists, count = 0;
1780         int extract_refs = 0;
1781         zend_array *symbol_table;
1782         zval var_array;
1783 
1784 #ifndef FAST_ZPP
1785         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|lz/", &var_array_param, &extract_type, &prefix) == FAILURE) {
1786                 return;
1787         }
1788 #else
1789         ZEND_PARSE_PARAMETERS_START(1, 3)
1790                 Z_PARAM_ARRAY(var_array_param)
1791                 Z_PARAM_OPTIONAL
1792                 Z_PARAM_LONG(extract_type)
1793                 Z_PARAM_ZVAL_EX(prefix, 0, 1)
1794         ZEND_PARSE_PARAMETERS_END();
1795 #endif
1796 
1797         extract_refs = (extract_type & EXTR_REFS);
1798         if (extract_refs) {
1799                 SEPARATE_ZVAL(var_array_param);
1800         }
1801         extract_type &= 0xff;
1802 
1803         if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
1804                 php_error_docref(NULL, E_WARNING, "Invalid extract type");
1805                 return;
1806         }
1807 
1808         if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
1809                 php_error_docref(NULL, E_WARNING, "specified extract type requires the prefix parameter");
1810                 return;
1811         }
1812 
1813         if (prefix) {
1814                 convert_to_string(prefix);
1815                 if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) {
1816                         php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier");
1817                         return;
1818                 }
1819         }
1820 
1821         symbol_table = zend_rebuild_symbol_table();
1822 #if 0
1823         if (!symbol_table) {
1824                 php_error_docref(NULL, E_WARNING, "failed to build symbol table");
1825                 return;
1826         }
1827 #endif
1828 
1829         /* The array might be stored in a local variable that will be overwritten. To avoid losing the
1830          * reference in that case we work on a copy. */
1831         ZVAL_COPY(&var_array, var_array_param);
1832 
1833         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(var_array), num_key, var_name, entry) {
1834                 zval final_name;
1835 
1836                 ZVAL_NULL(&final_name);
1837                 var_exists = 0;
1838 
1839                 if (var_name) {
1840                         var_exists = zend_hash_exists_ind(symbol_table, var_name);
1841                 } else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) {
1842                         zend_string *str = zend_long_to_str(num_key);
1843                         php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
1844                         zend_string_release(str);
1845                 } else {
1846                         continue;
1847                 }
1848 
1849                 switch (extract_type) {
1850                         case EXTR_IF_EXISTS:
1851                                 if (!var_exists) break;
1852                                 /* break omitted intentionally */
1853 
1854                         case EXTR_OVERWRITE:
1855                                 /* GLOBALS protection */
1856                                 if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) {
1857                                         break;
1858                                 }
1859                                 if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1  && !strcmp(ZSTR_VAL(var_name), "this") && EG(scope) && ZSTR_LEN(EG(scope)->name) != 0) {
1860                                         break;
1861                                 }
1862                                 ZVAL_STR_COPY(&final_name, var_name);
1863                                 break;
1864 
1865                         case EXTR_PREFIX_IF_EXISTS:
1866                                 if (var_exists) {
1867                                         php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1868                                 }
1869                                 break;
1870 
1871                         case EXTR_PREFIX_SAME:
1872                                 if (!var_exists && ZSTR_LEN(var_name) != 0) {
1873                                         ZVAL_STR_COPY(&final_name, var_name);
1874                                 }
1875                                 /* break omitted intentionally */
1876 
1877                         case EXTR_PREFIX_ALL:
1878                                 if (Z_TYPE(final_name) == IS_NULL && ZSTR_LEN(var_name) != 0) {
1879                                         php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1880                                 }
1881                                 break;
1882 
1883                         case EXTR_PREFIX_INVALID:
1884                                 if (Z_TYPE(final_name) == IS_NULL) {
1885                                         if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
1886                                                 php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
1887                                         } else {
1888                                                 ZVAL_STR_COPY(&final_name, var_name);
1889                                         }
1890                                 }
1891                                 break;
1892 
1893                         default:
1894                                 if (!var_exists) {
1895                                         ZVAL_STR_COPY(&final_name, var_name);
1896                                 }
1897                                 break;
1898                 }
1899 
1900                 if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1901                         zval *orig_var;
1902                         if (extract_refs) {
1903 
1904                                 ZVAL_MAKE_REF(entry);
1905                                 Z_ADDREF_P(entry);
1906 
1907                                 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1908                                         if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1909                                                 orig_var = Z_INDIRECT_P(orig_var);
1910                                         }
1911                                         zval_ptr_dtor(orig_var);
1912                                         ZVAL_COPY_VALUE(orig_var, entry);
1913                                 } else {
1914                                         zend_hash_update(symbol_table, Z_STR(final_name), entry);
1915                                 }
1916                         } else {
1917                                 ZVAL_DEREF(entry);
1918                                 if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
1919                                 if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
1920                                         if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1921                                                 orig_var = Z_INDIRECT_P(orig_var);
1922                                         }
1923                                         ZVAL_DEREF(orig_var);
1924                                         zval_ptr_dtor(orig_var);
1925                                         ZVAL_COPY_VALUE(orig_var, entry);
1926                                 } else {
1927                                         zend_hash_update(symbol_table, Z_STR(final_name), entry);
1928                                 }
1929                         }
1930                         count++;
1931                 }
1932                 zval_dtor(&final_name);
1933         } ZEND_HASH_FOREACH_END();
1934         zval_ptr_dtor(&var_array);
1935 
1936         RETURN_LONG(count);
1937 }
1938 /* }}} */
1939 
1940 static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry) /* {{{ */
1941 {
1942         zval *value_ptr, data;
1943 
1944         ZVAL_DEREF(entry);
1945         if (Z_TYPE_P(entry) == IS_STRING) {
1946                 if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
1947                         ZVAL_DEREF(value_ptr);
1948                         ZVAL_COPY(&data, value_ptr);
1949                         zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
1950                 }
1951         } else if (Z_TYPE_P(entry) == IS_ARRAY) {
1952                 if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) {
1953                         php_error_docref(NULL, E_WARNING, "recursion detected");
1954                         return;
1955                 }
1956 
1957             if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
1958                         Z_ARRVAL_P(entry)->u.v.nApplyCount++;
1959                 }
1960                 ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
1961                         php_compact_var(eg_active_symbol_table, return_value, value_ptr);
1962                 } ZEND_HASH_FOREACH_END();
1963             if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
1964                         Z_ARRVAL_P(entry)->u.v.nApplyCount--;
1965                 }
1966         }
1967 }
1968 /* }}} */
1969 
1970 /* {{{ proto array compact(mixed var_names [, mixed ...])
1971    Creates a hash containing variables and their values */
1972 PHP_FUNCTION(compact)
1973 {
1974         zval *args = NULL;      /* function arguments array */
1975         uint32_t num_args, i;
1976         zend_array *symbol_table;
1977 
1978         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) {
1979                 return;
1980         }
1981 
1982         symbol_table = zend_rebuild_symbol_table();
1983 
1984         if (UNEXPECTED(symbol_table == NULL)) {
1985                 return;
1986         }
1987 
1988         /* compact() is probably most used with a single array of var_names
1989            or multiple string names, rather than a combination of both.
1990            So quickly guess a minimum result size based on that */
1991         if (ZEND_NUM_ARGS() == 1 && Z_TYPE(args[0]) == IS_ARRAY) {
1992                 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
1993         } else {
1994                 array_init_size(return_value, ZEND_NUM_ARGS());
1995         }
1996 
1997         for (i=0; i<ZEND_NUM_ARGS(); i++) {
1998                 php_compact_var(symbol_table, return_value, &args[i]);
1999         }
2000 }
2001 /* }}} */
2002 
2003 /* {{{ proto array array_fill(int start_key, int num, mixed val)
2004    Create an array containing num elements starting with index start_key each initialized to val */
2005 PHP_FUNCTION(array_fill)
2006 {
2007         zval *val;
2008         zend_long start_key, num;
2009 
2010         if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &start_key, &num, &val) == FAILURE) {
2011                 return;
2012         }
2013 
2014         if (num < 0) {
2015                 php_error_docref(NULL, E_WARNING, "Number of elements can't be negative");
2016                 RETURN_FALSE;
2017         }
2018 
2019         /* allocate an array for return */
2020         array_init_size(return_value, (uint32_t)num);
2021 
2022         if (num == 0) {
2023                 return;
2024         }
2025 
2026         num--;
2027         zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, val);
2028         Z_TRY_ADDREF_P(val);
2029 
2030         while (num--) {
2031                 if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), val) != NULL) {
2032                         Z_TRY_ADDREF_P(val);
2033                 } else {
2034                         zval_dtor(return_value);
2035                         php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied");
2036                         RETURN_FALSE;
2037                 }
2038         }
2039 }
2040 /* }}} */
2041 
2042 /* {{{ proto array array_fill_keys(array keys, mixed val)
2043    Create an array using the elements of the first parameter as keys each initialized to val */
2044 PHP_FUNCTION(array_fill_keys)
2045 {
2046         zval *keys, *val, *entry;
2047 
2048         if (zend_parse_parameters(ZEND_NUM_ARGS(), "az", &keys, &val) == FAILURE) {
2049                 return;
2050         }
2051 
2052         /* Initialize return array */
2053         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2054 
2055         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2056                 ZVAL_DEREF(entry);
2057                 Z_TRY_ADDREF_P(val);
2058                 if (Z_TYPE_P(entry) == IS_LONG) {
2059                         zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2060                 } else {
2061                         zend_string *key = zval_get_string(entry);
2062                         zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2063                         zend_string_release(key);
2064                 }
2065         } ZEND_HASH_FOREACH_END();
2066 }
2067 /* }}} */
2068 
2069 #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
2070                 double __calc_size = ((start - end) / step) + 1; \
2071                 if (__calc_size >= (double)HT_MAX_SIZE) { \
2072                         php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
2073                         RETURN_FALSE; \
2074                 } \
2075                 size = (uint32_t)__calc_size; \
2076                 array_init_size(return_value, size); \
2077                 zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
2078         } while (0)
2079 
2080 #define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
2081                 zend_ulong __calc_size = (start - end) / lstep; \
2082                 if (__calc_size >= HT_MAX_SIZE - 1) { \
2083                         php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%pd end=%pd", end, start); \
2084                         RETURN_FALSE; \
2085                 } \
2086                 size = (uint32_t)(__calc_size + 1); \
2087                 array_init_size(return_value, size); \
2088                 zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
2089         } while (0)
2090 
2091 /* {{{ proto array range(mixed low, mixed high[, int step])
2092    Create an array containing the range of integers or characters from low to high (inclusive) */
2093 PHP_FUNCTION(range)
2094 {
2095         zval *zlow, *zhigh, *zstep = NULL, tmp;
2096         int err = 0, is_step_double = 0;
2097         double step = 1.0;
2098 
2099         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &zlow, &zhigh, &zstep) == FAILURE) {
2100                 RETURN_FALSE;
2101         }
2102 
2103         if (zstep) {
2104                 if (Z_TYPE_P(zstep) == IS_DOUBLE ||
2105                         (Z_TYPE_P(zstep) == IS_STRING && is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0) == IS_DOUBLE)
2106                 ) {
2107                         is_step_double = 1;
2108                 }
2109 
2110                 step = zval_get_double(zstep);
2111 
2112                 /* We only want positive step values. */
2113                 if (step < 0.0) {
2114                         step *= -1;
2115                 }
2116         }
2117 
2118         /* If the range is given as strings, generate an array of characters. */
2119         if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
2120                 int type1, type2;
2121                 unsigned char low, high;
2122                 zend_long lstep = (zend_long) step;
2123 
2124                 type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
2125                 type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
2126 
2127                 if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
2128                         goto double_str;
2129                 } else if (type1 == IS_LONG || type2 == IS_LONG) {
2130                         goto long_str;
2131                 }
2132 
2133                 low = (unsigned char)Z_STRVAL_P(zlow)[0];
2134                 high = (unsigned char)Z_STRVAL_P(zhigh)[0];
2135 
2136                 if (low > high) {               /* Negative Steps */
2137                         if (lstep <= 0) {
2138                                 err = 1;
2139                                 goto err;
2140                         }
2141                         /* Initialize the return_value as an array. */
2142                         array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1));
2143                         zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
2144                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2145                                 for (; low >= high; low -= (unsigned int)lstep) {
2146                                         if (CG(one_char_string)[low]) {
2147                                                 ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
2148                                         } else {
2149                                                 ZVAL_STRINGL(&tmp, (char*)&low, 1);
2150                                         }
2151                                         ZEND_HASH_FILL_ADD(&tmp);
2152                                         if (((signed int)low - lstep) < 0) {
2153                                                 break;
2154                                         }
2155                                 }
2156                         } ZEND_HASH_FILL_END();
2157                 } else if (high > low) {        /* Positive Steps */
2158                         if (lstep <= 0) {
2159                                 err = 1;
2160                                 goto err;
2161                         }
2162                         array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1));
2163                         zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
2164                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2165                                 for (; low <= high; low += (unsigned int)lstep) {
2166                                         if (CG(one_char_string)[low]) {
2167                                                 ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
2168                                         } else {
2169                                                 ZVAL_STRINGL(&tmp, (char*)&low, 1);
2170                                         }
2171                                         ZEND_HASH_FILL_ADD(&tmp);
2172                                         if (((signed int)low + lstep) > 255) {
2173                                                 break;
2174                                         }
2175                                 }
2176                         } ZEND_HASH_FILL_END();
2177                 } else {
2178                         array_init(return_value);
2179                         if (CG(one_char_string)[low]) {
2180                                 ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
2181                         } else {
2182                                 ZVAL_STRINGL(&tmp, (char*)&low, 1);
2183                         }
2184                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2185                 }
2186         } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
2187                 double low, high;
2188                 uint32_t i, size;
2189 double_str:
2190                 low = zval_get_double(zlow);
2191                 high = zval_get_double(zhigh);
2192 
2193                 if (zend_isinf(high) || zend_isinf(low)) {
2194                         php_error_docref(NULL, E_WARNING, "Invalid range supplied: start=%0.0f end=%0.0f", low, high);
2195                         RETURN_FALSE;
2196                 }
2197 
2198                 Z_TYPE_INFO(tmp) = IS_DOUBLE;
2199                 if (low > high) {               /* Negative steps */
2200                         if (low - high < step || step <= 0) {
2201                                 err = 1;
2202                                 goto err;
2203                         }
2204 
2205                         RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
2206 
2207                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2208                                 for (i = 0; i < size; ++i) {
2209                                         Z_DVAL(tmp) = low - (i * step);
2210                                         ZEND_HASH_FILL_ADD(&tmp);
2211                                 }
2212                         } ZEND_HASH_FILL_END();
2213                 } else if (high > low) {        /* Positive steps */
2214                         if (high - low < step || step <= 0) {
2215                                 err = 1;
2216                                 goto err;
2217                         }
2218 
2219                         RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
2220 
2221                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2222                                 for (i = 0; i < size; ++i) {
2223                                         Z_DVAL(tmp) = low + (i * step);
2224                                         ZEND_HASH_FILL_ADD(&tmp);
2225                                 }
2226                         } ZEND_HASH_FILL_END();
2227                 } else {
2228                         array_init(return_value);
2229                         Z_DVAL(tmp) = low;
2230                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2231                 }
2232         } else {
2233                 zend_long low, high;
2234                 /* lstep is a ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
2235                 zend_ulong lstep;
2236                 uint32_t i, size;
2237 long_str:
2238                 low = zval_get_long(zlow);
2239                 high = zval_get_long(zhigh);
2240 
2241                 if (step <= 0) {
2242                         err = 1;
2243                         goto err;
2244                 }
2245 
2246                 lstep = step;
2247 
2248                 Z_TYPE_INFO(tmp) = IS_LONG;
2249                 if (low > high) {               /* Negative steps */
2250                         if (low - high < lstep) {
2251                                 err = 1;
2252                                 goto err;
2253                         }
2254 
2255                         RANGE_CHECK_LONG_INIT_ARRAY(low, high);
2256 
2257                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2258                                 for (i = 0; i < size; ++i) {
2259                                         Z_LVAL(tmp) = low - (i * lstep);
2260                                         ZEND_HASH_FILL_ADD(&tmp);
2261                                 }
2262                         } ZEND_HASH_FILL_END();
2263                 } else if (high > low) {        /* Positive steps */
2264                         if (high - low < lstep) {
2265                                 err = 1;
2266                                 goto err;
2267                         }
2268 
2269                         RANGE_CHECK_LONG_INIT_ARRAY(high, low);
2270 
2271                         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2272                                 for (i = 0; i < size; ++i) {
2273                                         Z_LVAL(tmp) = low + (i * lstep);
2274                                         ZEND_HASH_FILL_ADD(&tmp);
2275                                 }
2276                         } ZEND_HASH_FILL_END();
2277                 } else {
2278                         array_init(return_value);
2279                         Z_LVAL(tmp) = low;
2280                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
2281                 }
2282         }
2283 err:
2284         if (err) {
2285                 php_error_docref(NULL, E_WARNING, "step exceeds the specified range");
2286                 RETURN_FALSE;
2287         }
2288 }
2289 /* }}} */
2290 
2291 #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
2292 #undef RANGE_CHECK_LONG_INIT_ARRAY
2293 
2294 static void php_array_data_shuffle(zval *array) /* {{{ */
2295 {
2296         uint32_t idx, j, n_elems;
2297         Bucket *p, temp;
2298         HashTable *hash;
2299         zend_long rnd_idx;
2300         uint32_t n_left;
2301 
2302         n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
2303 
2304         if (n_elems < 1) {
2305                 return;
2306         }
2307 
2308         hash = Z_ARRVAL_P(array);
2309         n_left = n_elems;
2310 
2311         if (EXPECTED(hash->u.v.nIteratorsCount == 0)) {
2312                 if (hash->nNumUsed != hash->nNumOfElements) {
2313                         for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2314                                 p = hash->arData + idx;
2315                                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2316                                 if (j != idx) {
2317                                         hash->arData[j] = *p;
2318                                 }
2319                                 j++;
2320                         }
2321                 }
2322                 while (--n_left) {
2323                         rnd_idx = php_rand();
2324                         RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
2325                         if (rnd_idx != n_left) {
2326                                 temp = hash->arData[n_left];
2327                                 hash->arData[n_left] = hash->arData[rnd_idx];
2328                                 hash->arData[rnd_idx] = temp;
2329                         }
2330                 }
2331         } else {
2332                 uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
2333 
2334                 if (hash->nNumUsed != hash->nNumOfElements) {
2335                         for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
2336                                 p = hash->arData + idx;
2337                                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2338                                 if (j != idx) {
2339                                         hash->arData[j] = *p;
2340                                         if (idx == iter_pos) {
2341                                                 zend_hash_iterators_update(hash, idx, j);
2342                                                 iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
2343                                         }
2344                                 }
2345                                 j++;
2346                         }
2347                 }
2348                 while (--n_left) {
2349                         rnd_idx = php_rand();
2350                         RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
2351                         if (rnd_idx != n_left) {
2352                                 temp = hash->arData[n_left];
2353                                 hash->arData[n_left] = hash->arData[rnd_idx];
2354                                 hash->arData[rnd_idx] = temp;
2355                                 zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
2356                         }
2357                 }
2358         }
2359         HANDLE_BLOCK_INTERRUPTIONS();
2360         hash->nNumUsed = n_elems;
2361         hash->nInternalPointer = 0;
2362 
2363         for (j = 0; j < n_elems; j++) {
2364                 p = hash->arData + j;
2365                 if (p->key) {
2366                         zend_string_release(p->key);
2367                 }
2368                 p->h = j;
2369                 p->key = NULL;
2370         }
2371         hash->nNextFreeElement = n_elems;
2372         if (!(hash->u.flags & HASH_FLAG_PACKED)) {
2373                 zend_hash_to_packed(hash);
2374         }
2375         HANDLE_UNBLOCK_INTERRUPTIONS();
2376 }
2377 /* }}} */
2378 
2379 /* {{{ proto bool shuffle(array array_arg)
2380    Randomly shuffle the contents of an array */
2381 PHP_FUNCTION(shuffle)
2382 {
2383         zval *array;
2384 
2385         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) {
2386                 RETURN_FALSE;
2387         }
2388 
2389         php_array_data_shuffle(array);
2390 
2391         RETURN_TRUE;
2392 }
2393 /* }}} */
2394 
2395 static void php_splice(HashTable *in_hash, int offset, int length, HashTable *replace, HashTable *removed) /* {{{ */
2396 {
2397         HashTable        out_hash;                      /* Output hashtable */
2398         int                      num_in,                        /* Number of entries in the input hashtable */
2399                                  pos;                           /* Current position in the hashtable */
2400         uint         idx;
2401         Bucket          *p;                                     /* Pointer to hash bucket */
2402         zval            *entry;                         /* Hash entry */
2403         uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
2404 
2405         /* Get number of entries in the input hash */
2406         num_in = zend_hash_num_elements(in_hash);
2407 
2408         /* Clamp the offset.. */
2409         if (offset > num_in) {
2410                 offset = num_in;
2411         } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2412                 offset = 0;
2413         }
2414 
2415         /* ..and the length */
2416         if (length < 0) {
2417                 length = num_in - offset + length;
2418         } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
2419                 length = num_in - offset;
2420         }
2421 
2422         /* Create and initialize output hash */
2423         zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
2424 
2425         /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
2426         for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
2427                 p = in_hash->arData + idx;
2428                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2429                 /* Get entry and increase reference count */
2430                 entry = &p->val;
2431 
2432                 /* Update output hash depending on key type */
2433                 if (p->key == NULL) {
2434                         zend_hash_next_index_insert_new(&out_hash, entry);
2435                 } else {
2436                         zend_hash_add_new(&out_hash, p->key, entry);
2437                 }
2438                 if (idx == iter_pos) {
2439                         if (idx != pos) {
2440                                 zend_hash_iterators_update(in_hash, idx, pos);
2441                         }
2442                         iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
2443                 }
2444                 pos++;
2445         }
2446 
2447         /* If hash for removed entries exists, go until offset+length and copy the entries to it */
2448         if (removed != NULL) {
2449                 for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
2450                         p = in_hash->arData + idx;
2451                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
2452                         pos++;
2453                         entry = &p->val;
2454                         if (Z_REFCOUNTED_P(entry)) {
2455                                 Z_ADDREF_P(entry);
2456                         }
2457                         if (p->key == NULL) {
2458                                 zend_hash_next_index_insert_new(removed, entry);
2459                                 zend_hash_index_del(in_hash, p->h);
2460                         } else {
2461                                 zend_hash_add_new(removed, p->key, entry);
2462                                 if (in_hash == &EG(symbol_table)) {
2463                                         zend_delete_global_variable(p->key);
2464                                 } else {
2465                                         zend_hash_del(in_hash, p->key);
2466                                 }
2467                         }
2468                 }
2469         } else { /* otherwise just skip those entries */
2470                 int pos2 = pos;
2471 
2472                 for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
2473                         p = in_hash->arData + idx;
2474                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
2475                         pos2++;
2476                         if (p->key == NULL) {
2477                                 zend_hash_index_del(in_hash, p->h);
2478                         } else {
2479                                 if (in_hash == &EG(symbol_table)) {
2480                                         zend_delete_global_variable(p->key);
2481                                 } else {
2482                                         zend_hash_del(in_hash, p->key);
2483                                 }
2484                         }
2485                 }
2486         }
2487         iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
2488 
2489         /* If there are entries to insert.. */
2490         if (replace) {
2491                 ZEND_HASH_FOREACH_VAL_IND(replace, entry) {
2492                         if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
2493                         zend_hash_next_index_insert_new(&out_hash, entry);
2494                         pos++;
2495                 } ZEND_HASH_FOREACH_END();
2496         }
2497 
2498         /* Copy the remaining input hash entries to the output hash */
2499         for ( ; idx < in_hash->nNumUsed ; idx++) {
2500                 p = in_hash->arData + idx;
2501                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2502                 entry = &p->val;
2503                 if (p->key == NULL) {
2504                         zend_hash_next_index_insert_new(&out_hash, entry);
2505                 } else {
2506                         zend_hash_add_new(&out_hash, p->key, entry);
2507                 }
2508                 if (idx == iter_pos) {
2509                         if (idx != pos) {
2510                                 zend_hash_iterators_update(in_hash, idx, pos);
2511                         }
2512                         iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
2513                 }
2514                 pos++;
2515         }
2516 
2517         /* replace HashTable data */
2518         in_hash->u.v.nIteratorsCount = 0;
2519         in_hash->pDestructor = NULL;
2520         zend_hash_destroy(in_hash);
2521 
2522         in_hash->u.v.flags         = out_hash.u.v.flags;
2523         in_hash->nTableSize        = out_hash.nTableSize;
2524         in_hash->nTableMask        = out_hash.nTableMask;
2525         in_hash->nNumUsed          = out_hash.nNumUsed;
2526         in_hash->nNumOfElements    = out_hash.nNumOfElements;
2527         in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
2528         in_hash->arData            = out_hash.arData;
2529         in_hash->pDestructor       = out_hash.pDestructor;
2530 
2531         zend_hash_internal_pointer_reset(in_hash);
2532 }
2533 /* }}} */
2534 
2535 /* {{{ proto int array_push(array stack, mixed var [, mixed ...])
2536    Pushes elements onto the end of the array */
2537 PHP_FUNCTION(array_push)
2538 {
2539         zval   *args,           /* Function arguments array */
2540                    *stack,              /* Input array */
2541                     new_var;    /* Variable to be pushed */
2542         int i,                          /* Loop counter */
2543                 argc;                   /* Number of function arguments */
2544 
2545 
2546         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) {
2547                 return;
2548         }
2549 
2550         /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
2551         for (i = 0; i < argc; i++) {
2552                 ZVAL_COPY(&new_var, &args[i]);
2553 
2554                 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
2555                         if (Z_REFCOUNTED(new_var)) Z_DELREF(new_var);
2556                         php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied");
2557                         RETURN_FALSE;
2558                 }
2559         }
2560 
2561         /* Clean up and return the number of values in the stack */
2562         RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
2563 }
2564 /* }}} */
2565 
2566 /* {{{ proto mixed array_pop(array stack)
2567    Pops an element off the end of the array */
2568 PHP_FUNCTION(array_pop)
2569 {
2570         zval *stack,    /* Input stack */
2571                  *val;          /* Value to be popped */
2572         uint32_t idx;
2573         Bucket *p;
2574 
2575 #ifndef FAST_ZPP
2576         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &stack) == FAILURE) {
2577                 return;
2578         }
2579 #else
2580         ZEND_PARSE_PARAMETERS_START(1, 1)
2581                 Z_PARAM_ARRAY_EX(stack, 0, 1)
2582         ZEND_PARSE_PARAMETERS_END();
2583 #endif
2584 
2585         if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
2586                 return;
2587         }
2588 
2589         /* Get the last value and copy it into the return value */
2590         idx = Z_ARRVAL_P(stack)->nNumUsed;
2591         while (1) {
2592                 if (idx == 0) {
2593                         return;
2594                 }
2595                 idx--;
2596                 p = Z_ARRVAL_P(stack)->arData + idx;
2597                 val = &p->val;
2598                 if (Z_TYPE_P(val) == IS_INDIRECT) {
2599                         val = Z_INDIRECT_P(val);
2600                 }
2601                 if (Z_TYPE_P(val) != IS_UNDEF) {
2602                         break;
2603                 }
2604         }
2605         ZVAL_DEREF(val);
2606         ZVAL_COPY(return_value, val);
2607 
2608         if (!p->key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
2609                 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
2610         }
2611 
2612         /* Delete the last value */
2613         if (p->key) {
2614                 if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
2615                         zend_delete_global_variable(p->key);
2616                 } else {
2617                         zend_hash_del(Z_ARRVAL_P(stack), p->key);
2618                 }
2619         } else {
2620                 zend_hash_index_del(Z_ARRVAL_P(stack), p->h);
2621         }
2622 
2623         zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2624 }
2625 /* }}} */
2626 
2627 /* {{{ proto mixed array_shift(array stack)
2628    Pops an element off the beginning of the array */
2629 PHP_FUNCTION(array_shift)
2630 {
2631         zval *stack,    /* Input stack */
2632                  *val;          /* Value to be popped */
2633         uint32_t idx;
2634         Bucket *p;
2635 
2636 #ifndef FAST_ZPP
2637         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &stack) == FAILURE) {
2638                 return;
2639         }
2640 #else
2641         ZEND_PARSE_PARAMETERS_START(1, 1)
2642                 Z_PARAM_ARRAY_EX(stack, 0, 1)
2643         ZEND_PARSE_PARAMETERS_END();
2644 #endif
2645 
2646         if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
2647                 return;
2648         }
2649 
2650         /* Get the first value and copy it into the return value */
2651         idx = 0;
2652         while (1) {
2653                 if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
2654                         return;
2655                 }
2656                 p = Z_ARRVAL_P(stack)->arData + idx;
2657                 val = &p->val;
2658                 if (Z_TYPE_P(val) == IS_INDIRECT) {
2659                         val = Z_INDIRECT_P(val);
2660                 }
2661                 if (Z_TYPE_P(val) != IS_UNDEF) {
2662                         break;
2663                 }
2664                 idx++;
2665         }
2666         ZVAL_DEREF(val);
2667         ZVAL_COPY(return_value, val);
2668 
2669         /* Delete the first value */
2670         if (p->key) {
2671                 if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
2672                         zend_delete_global_variable(p->key);
2673                 } else {
2674                         zend_hash_del(Z_ARRVAL_P(stack), p->key);
2675                 }
2676         } else {
2677                 zend_hash_index_del(Z_ARRVAL_P(stack), p->h);
2678         }
2679 
2680         /* re-index like it did before */
2681         if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) {
2682                 uint32_t k = 0;
2683 
2684                 if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
2685                         for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
2686                                 p = Z_ARRVAL_P(stack)->arData + idx;
2687                                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2688                                 if (idx != k) {
2689                                         Bucket *q = Z_ARRVAL_P(stack)->arData + k;
2690                                         q->h = k;
2691                                         q->key = NULL;
2692                                         ZVAL_COPY_VALUE(&q->val, &p->val);
2693                                         ZVAL_UNDEF(&p->val);
2694                                 }
2695                                 k++;
2696                         }
2697                 } else {
2698                         uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
2699 
2700                         for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
2701                                 p = Z_ARRVAL_P(stack)->arData + idx;
2702                                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
2703                                 if (idx != k) {
2704                                         Bucket *q = Z_ARRVAL_P(stack)->arData + k;
2705                                         q->h = k;
2706                                         q->key = NULL;
2707                                         ZVAL_COPY_VALUE(&q->val, &p->val);
2708                                         ZVAL_UNDEF(&p->val);
2709                                         if (idx == iter_pos) {
2710                                                 zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
2711                                                 iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
2712                                         }
2713                                 }
2714                                 k++;
2715                         }
2716                 }
2717                 Z_ARRVAL_P(stack)->nNumUsed = k;
2718                 Z_ARRVAL_P(stack)->nNextFreeElement = k;
2719         } else {
2720                 uint32_t k = 0;
2721                 int should_rehash = 0;
2722 
2723                 for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
2724                         p = Z_ARRVAL_P(stack)->arData + idx;
2725                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
2726                         if (p->key == NULL) {
2727                                 if (p->h != k) {
2728                                         p->h = k++;
2729                                         should_rehash = 1;
2730                                 } else {
2731                                         k++;
2732                                 }
2733                         }
2734                 }
2735                 Z_ARRVAL_P(stack)->nNextFreeElement = k;
2736                 if (should_rehash) {
2737                         zend_hash_rehash(Z_ARRVAL_P(stack));
2738                 }
2739         }
2740 
2741         zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2742 }
2743 /* }}} */
2744 
2745 /* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
2746    Pushes elements onto the beginning of the array */
2747 PHP_FUNCTION(array_unshift)
2748 {
2749         zval   *args,                   /* Function arguments array */
2750                    *stack;                      /* Input stack */
2751         HashTable new_hash;             /* New hashtable for the stack */
2752         int argc;                               /* Number of function arguments */
2753         int i;
2754         zend_string *key;
2755         zval *value;
2756 
2757         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) {
2758                 return;
2759         }
2760 
2761         zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
2762         for (i = 0; i < argc; i++) {
2763                 if (Z_REFCOUNTED(args[i])) {
2764                         Z_ADDREF(args[i]);
2765                 }
2766                 zend_hash_next_index_insert_new(&new_hash, &args[i]);
2767         }
2768         if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
2769                 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
2770                         if (key) {
2771                                 zend_hash_add_new(&new_hash, key, value);
2772                         } else {
2773                                 zend_hash_next_index_insert_new(&new_hash, value);
2774                         }
2775                 } ZEND_HASH_FOREACH_END();
2776         } else {
2777                 uint32_t old_idx;
2778                 uint32_t new_idx = i;
2779                 uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
2780 
2781                 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
2782                         if (key) {
2783                                 zend_hash_add_new(&new_hash, key, value);
2784                         } else {
2785                                 zend_hash_next_index_insert_new(&new_hash, value);
2786                         }
2787                         old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData;
2788                         if (old_idx == iter_pos) {
2789                                 zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx);
2790                                 iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
2791                         }
2792                         new_idx++;
2793                 } ZEND_HASH_FOREACH_END();
2794         }
2795 
2796         /* replace HashTable data */
2797         Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0;
2798         Z_ARRVAL_P(stack)->pDestructor = NULL;
2799         zend_hash_destroy(Z_ARRVAL_P(stack));
2800 
2801         Z_ARRVAL_P(stack)->u.v.flags         = new_hash.u.v.flags;
2802         Z_ARRVAL_P(stack)->nTableSize        = new_hash.nTableSize;
2803         Z_ARRVAL_P(stack)->nTableMask        = new_hash.nTableMask;
2804         Z_ARRVAL_P(stack)->nNumUsed          = new_hash.nNumUsed;
2805         Z_ARRVAL_P(stack)->nNumOfElements    = new_hash.nNumOfElements;
2806         Z_ARRVAL_P(stack)->nNextFreeElement  = new_hash.nNextFreeElement;
2807         Z_ARRVAL_P(stack)->arData            = new_hash.arData;
2808         Z_ARRVAL_P(stack)->pDestructor       = new_hash.pDestructor;
2809         
2810         zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
2811 
2812         /* Clean up and return the number of elements in the stack */
2813         RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
2814 }
2815 /* }}} */
2816 
2817 /* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
2818    Removes the elements designated by offset and length and replace them with supplied array */
2819 PHP_FUNCTION(array_splice)
2820 {
2821         zval *array,                            /* Input array */
2822                  *repl_array = NULL;    /* Replacement array */
2823         HashTable  *rem_hash = NULL;
2824         zend_long offset,
2825                         length = 0;
2826         int             num_in;                         /* Number of elements in the input array */
2827 
2828         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/l|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
2829                 return;
2830         }
2831 
2832         num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
2833 
2834         if (ZEND_NUM_ARGS() < 3) {
2835                 length = num_in;
2836         }
2837 
2838         if (ZEND_NUM_ARGS() == 4) {
2839                 /* Make sure the last argument, if passed, is an array */
2840                 convert_to_array_ex(repl_array);
2841         }
2842 
2843         /* Don't create the array of removed elements if it's not going
2844          * to be used; e.g. only removing and/or replacing elements */
2845         if (USED_RET()) {
2846                 zend_long size = length;
2847 
2848                 /* Clamp the offset.. */
2849                 if (offset > num_in) {
2850                         offset = num_in;
2851                 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2852                         offset = 0;
2853                 }
2854 
2855                 /* ..and the length */
2856                 if (length < 0) {
2857                         size = num_in - offset + length;
2858                 } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
2859                         size = num_in - offset;
2860                 }
2861 
2862                 /* Initialize return value */
2863                 array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
2864                 rem_hash = Z_ARRVAL_P(return_value);
2865         }
2866 
2867         /* Perform splice */
2868         php_splice(Z_ARRVAL_P(array), (int)offset, (int)length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
2869 }
2870 /* }}} */
2871 
2872 /* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
2873    Returns elements specified by offset and length */
2874 PHP_FUNCTION(array_slice)
2875 {
2876         zval     *input,                /* Input array */
2877                          *z_length = NULL, /* How many elements to get */
2878                          *entry;                /* An array entry */
2879         zend_long        offset,                /* Offset to get elements from */
2880                          length = 0;
2881         zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */
2882         int              num_in,                /* Number of elements in the input array */
2883                          pos;                   /* Current position in the array */
2884         zend_string *string_key;
2885         zend_ulong num_key;
2886 
2887 #ifndef FAST_ZPP
2888         if (zend_parse_parameters(ZEND_NUM_ARGS(), "al|zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
2889                 return;
2890         }
2891 #else
2892         ZEND_PARSE_PARAMETERS_START(2, 4)
2893                 Z_PARAM_ARRAY(input)
2894                 Z_PARAM_LONG(offset)
2895                 Z_PARAM_OPTIONAL
2896                 Z_PARAM_ZVAL(z_length)
2897                 Z_PARAM_BOOL(preserve_keys)
2898         ZEND_PARSE_PARAMETERS_END();
2899 #endif
2900 
2901         /* Get number of entries in the input hash */
2902         num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
2903 
2904         /* We want all entries from offset to the end if length is not passed or is null */
2905         if (ZEND_NUM_ARGS() < 3 || Z_TYPE_P(z_length) == IS_NULL) {
2906                 length = num_in;
2907         } else {
2908                 length = zval_get_long(z_length);
2909         }
2910 
2911         /* Clamp the offset.. */
2912         if (offset > num_in) {
2913                 array_init(return_value);
2914                 return;
2915         } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2916                 offset = 0;
2917         }
2918 
2919         /* ..and the length */
2920         if (length < 0) {
2921                 length = num_in - offset + length;
2922         } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
2923                 length = num_in - offset;
2924         }
2925 
2926         if (length <= 0) {
2927                 array_init(return_value);
2928                 return;
2929         }
2930 
2931         /* Initialize returned array */
2932         array_init_size(return_value, (uint32_t)length);
2933 
2934         /* Start at the beginning and go until we hit offset */
2935         pos = 0;
2936         if (!preserve_keys && (Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED)) {
2937                 zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
2938                 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2939                         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
2940                                 pos++;
2941                                 if (pos <= offset) {
2942                                         continue;
2943                                 }
2944                                 if (pos > offset + length) {
2945                                         break;
2946                                 }
2947                                 ZEND_HASH_FILL_ADD(entry);
2948                                 zval_add_ref(entry);
2949                         } ZEND_HASH_FOREACH_END();
2950                 } ZEND_HASH_FILL_END();
2951         } else {
2952                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
2953                         pos++;
2954                         if (pos <= offset) {
2955                                 continue;
2956                         }
2957                         if (pos > offset + length) {
2958                                 break;
2959                         }
2960 
2961                         if (string_key) {
2962                                 entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
2963                         } else {
2964                                 if (preserve_keys) {
2965                                         entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
2966                                 } else {
2967                                         entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
2968                                 }
2969                         }
2970                         zval_add_ref(entry);
2971                 } ZEND_HASH_FOREACH_END();
2972         }
2973 }
2974 /* }}} */
2975 
2976 PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
2977 {
2978         zval *src_entry, *dest_entry;
2979         zend_string *string_key;
2980 
2981         ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
2982                 if (string_key) {
2983                         if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) {
2984                                 zval *src_zval = src_entry;
2985                                 zval *dest_zval = dest_entry;
2986                                 HashTable *thash;
2987                                 zval tmp;
2988                                 int ret;
2989 
2990                                 ZVAL_DEREF(src_zval);
2991                                 ZVAL_DEREF(dest_zval);
2992                                 thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
2993                                 if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
2994                                         php_error_docref(NULL, E_WARNING, "recursion detected");
2995                                         return 0;
2996                                 }
2997 
2998                                 if (Z_ISREF_P(dest_entry)) {
2999                                         if (Z_REFCOUNT_P(dest_entry) == 1) {
3000                                                 ZVAL_UNREF(dest_entry);
3001                                         } else {
3002                                                 Z_DELREF_P(dest_entry);
3003                                                 ZVAL_DUP(dest_entry, dest_zval);
3004                                         }
3005                                         dest_zval = dest_entry;
3006                                 } else {
3007                                         SEPARATE_ZVAL(dest_zval);
3008                                 }
3009                                 if (Z_TYPE_P(dest_zval) == IS_NULL) {
3010                                         convert_to_array_ex(dest_zval);
3011                                         add_next_index_null(dest_zval);
3012                                 } else if (Z_TYPE_P(dest_zval) == IS_ARRAY) {
3013                                         if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > Z_ARRVAL_P(dest_zval)->nNumUsed)) {
3014                                                 Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed;
3015                                         }
3016                                 } else {
3017                                         convert_to_array_ex(dest_zval);
3018                                 }
3019                                 ZVAL_UNDEF(&tmp);
3020                                 if (Z_TYPE_P(src_zval) == IS_OBJECT) {
3021                                         ZVAL_COPY(&tmp, src_zval);
3022                                         convert_to_array(&tmp);
3023                                         src_zval = &tmp;
3024                                 }
3025                                 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3026                                         if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
3027                                                 thash->u.v.nApplyCount++;
3028                                         }
3029                                         ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3030                                         if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
3031                                                 thash->u.v.nApplyCount--;
3032                                         }
3033                                         if (!ret) {
3034                                                 return 0;
3035                                         }
3036                                 } else {
3037                                         if (Z_REFCOUNTED_P(src_entry)) {
3038                                                 Z_ADDREF_P(src_entry);
3039                                         }
3040                                         zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
3041                                 }
3042                                 zval_ptr_dtor(&tmp);
3043                         } else {
3044                                 if (Z_REFCOUNTED_P(src_entry)) {
3045                                         Z_ADDREF_P(src_entry);
3046                                 }
3047                                 zend_hash_add_new(dest, string_key, src_entry);
3048                         }
3049                 } else {
3050                         if (Z_REFCOUNTED_P(src_entry)) {
3051                                 Z_ADDREF_P(src_entry);
3052                         }
3053                         zend_hash_next_index_insert_new(dest, src_entry);
3054                 }
3055         } ZEND_HASH_FOREACH_END();
3056         return 1;
3057 }
3058 /* }}} */
3059 
3060 PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
3061 {
3062         zval *src_entry;
3063         zend_string *string_key;
3064 
3065         ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3066                 if (string_key) {
3067                         if (Z_REFCOUNTED_P(src_entry)) {
3068                                 Z_ADDREF_P(src_entry);
3069                         }
3070                         zend_hash_update(dest, string_key, src_entry);
3071                 } else {
3072                         if (Z_REFCOUNTED_P(src_entry)) {
3073                                 Z_ADDREF_P(src_entry);
3074                         }
3075                         zend_hash_next_index_insert_new(dest, src_entry);
3076                 }
3077         } ZEND_HASH_FOREACH_END();
3078         return 1;
3079 }
3080 /* }}} */
3081 
3082 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
3083 {
3084         zval *src_entry, *dest_entry, *src_zval, *dest_zval;
3085         zend_string *string_key;
3086         zend_ulong num_key;
3087         int ret;
3088 
3089         ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
3090                 src_zval = src_entry;
3091                 ZVAL_DEREF(src_zval);
3092                 if (string_key) {
3093                         if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3094                                 (dest_entry = zend_hash_find(dest, string_key)) == NULL ||
3095                                 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
3096                                  (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3097 
3098                                 if (Z_REFCOUNTED_P(src_entry)) {
3099                                         Z_ADDREF_P(src_entry);
3100                                 }
3101                                 zend_hash_update(dest, string_key, src_entry);
3102 
3103                                 continue;
3104                         }
3105                 } else {
3106                         if (Z_TYPE_P(src_zval) != IS_ARRAY ||
3107                                 (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
3108                                 (Z_TYPE_P(dest_entry) != IS_ARRAY &&
3109                                  (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
3110 
3111                                 if (Z_REFCOUNTED_P(src_entry)) {
3112                                         Z_ADDREF_P(src_entry);
3113                                 }
3114                                 zend_hash_index_update(dest, num_key, src_entry);
3115 
3116                                 continue;
3117                         }
3118                 }
3119 
3120                 dest_zval = dest_entry;
3121                 ZVAL_DEREF(dest_zval);
3122                 if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 ||
3123                     Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 ||
3124                     (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
3125                         php_error_docref(NULL, E_WARNING, "recursion detected");
3126                         return 0;
3127                 }
3128                 SEPARATE_ZVAL(dest_zval);
3129 
3130                 if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
3131                         Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++;
3132                 }
3133                 if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
3134                         Z_ARRVAL_P(src_zval)->u.v.nApplyCount++;
3135                 }
3136 
3137                 ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3138 
3139                 if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
3140                         Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--;
3141                 }
3142                 if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
3143                         Z_ARRVAL_P(src_zval)->u.v.nApplyCount--;
3144                 }
3145 
3146                 if (!ret) {
3147                         return 0;
3148                 }
3149         } ZEND_HASH_FOREACH_END();
3150 
3151         return 1;
3152 }
3153 /* }}} */
3154 
3155 static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
3156 {
3157         zval *args = NULL;
3158         zval *arg;
3159         int argc, i, init_size = 0;
3160 
3161 #ifndef FAST_ZPP
3162         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
3163                 return;
3164         }
3165 #else
3166         ZEND_PARSE_PARAMETERS_START(1, -1)
3167                 Z_PARAM_VARIADIC('+', args, argc)
3168         ZEND_PARSE_PARAMETERS_END();
3169 #endif
3170 
3171         for (i = 0; i < argc; i++) {
3172                 zval *arg = args + i;
3173 
3174                 ZVAL_DEREF(arg);
3175                 if (Z_TYPE_P(arg) != IS_ARRAY) {
3176                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
3177                         RETURN_NULL();
3178                 } else {
3179                         int num = zend_hash_num_elements(Z_ARRVAL_P(arg));
3180 
3181                         if (num > init_size) {
3182                                 init_size = num;
3183                         }
3184                 }
3185         }
3186 
3187         array_init_size(return_value, init_size);
3188 
3189         if (replace) {
3190                 zend_string *string_key;
3191                 zval *src_entry;
3192                 zend_ulong idx;
3193                 HashTable *src, *dest;
3194 
3195                 /* copy first array */
3196                 arg = args;
3197                 ZVAL_DEREF(arg);
3198                 src  = Z_ARRVAL_P(arg);
3199                 dest = Z_ARRVAL_P(return_value);
3200                 ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) {
3201                         if (string_key) {
3202                                 if (Z_REFCOUNTED_P(src_entry)) {
3203                                         Z_ADDREF_P(src_entry);
3204                                 }
3205                                 zend_hash_add_new(dest, string_key, src_entry);
3206                         } else {
3207                                 if (Z_REFCOUNTED_P(src_entry)) {
3208                                         Z_ADDREF_P(src_entry);
3209                                 }
3210                                 zend_hash_index_add_new(dest, idx, src_entry);
3211                         }
3212                 } ZEND_HASH_FOREACH_END();
3213 
3214                 if (recursive) {
3215                         for (i = 1; i < argc; i++) {
3216                                 arg = args + i;
3217                                 ZVAL_DEREF(arg);
3218                                 php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
3219                         }
3220                 } else {
3221                         for (i = 1; i < argc; i++) {
3222                                 arg = args + i;
3223                                 ZVAL_DEREF(arg);
3224                                 zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1);
3225                         }
3226                 }
3227         } else {
3228                 zend_string *string_key;
3229                 zval *src_entry;
3230                 HashTable *src, *dest;
3231 
3232                 /* copy first array */
3233                 arg = args;
3234                 ZVAL_DEREF(arg);
3235                 src  = Z_ARRVAL_P(arg);
3236                 dest = Z_ARRVAL_P(return_value);
3237                 ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3238                         if (string_key) {
3239                                 if (Z_REFCOUNTED_P(src_entry)) {
3240                                         Z_ADDREF_P(src_entry);
3241                                 }
3242                                 zend_hash_add_new(dest, string_key, src_entry);
3243                         } else {
3244                                 if (Z_REFCOUNTED_P(src_entry)) {
3245                                         Z_ADDREF_P(src_entry);
3246                                 }
3247                                 zend_hash_next_index_insert_new(dest, src_entry);
3248                         }
3249                 } ZEND_HASH_FOREACH_END();
3250 
3251                 if (recursive) {
3252                         for (i = 1; i < argc; i++) {
3253                                 arg = args + i;
3254                                 ZVAL_DEREF(arg);
3255                                 php_array_merge_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
3256                         }
3257                 } else {
3258                         for (i = 1; i < argc; i++) {
3259                                 arg = args + i;
3260                                 ZVAL_DEREF(arg);
3261                                 php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg));
3262                         }
3263                 }
3264         }
3265 }
3266 /* }}} */
3267 
3268 /* {{{ proto array array_merge(array arr1, array arr2 [, array ...])
3269    Merges elements from passed arrays into one array */
3270 PHP_FUNCTION(array_merge)
3271 {
3272         php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
3273 }
3274 /* }}} */
3275 
3276 /* {{{ proto array array_merge_recursive(array arr1, array arr2 [, array ...])
3277    Recursively merges elements from passed arrays into one array */
3278 PHP_FUNCTION(array_merge_recursive)
3279 {
3280         php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
3281 }
3282 /* }}} */
3283 
3284 /* {{{ proto array array_replace(array arr1, array arr2 [, array ...])
3285    Replaces elements from passed arrays into one array */
3286 PHP_FUNCTION(array_replace)
3287 {
3288         php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
3289 }
3290 /* }}} */
3291 
3292 /* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...])
3293    Recursively replaces elements from passed arrays into one array */
3294 PHP_FUNCTION(array_replace_recursive)
3295 {
3296         php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
3297 }
3298 /* }}} */
3299 
3300 /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
3301    Return just the keys from the input array, optionally only for the specified search_value */
3302 PHP_FUNCTION(array_keys)
3303 {
3304         zval *input,                            /* Input array */
3305              *search_value = NULL,      /* Value to search for */
3306              *entry,                            /* An entry in the input array */
3307                new_val;                         /* New value */
3308         zend_bool strict = 0;           /* do strict comparison */
3309         zend_ulong num_idx;
3310         zend_string *str_idx;
3311 
3312 #ifndef FAST_ZPP
3313         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|zb", &input, &search_value, &strict) == FAILURE) {
3314                 return;
3315         }
3316 #else
3317         ZEND_PARSE_PARAMETERS_START(1, 3)
3318                 Z_PARAM_ARRAY(input)
3319                 Z_PARAM_OPTIONAL
3320                 Z_PARAM_ZVAL(search_value)
3321                 Z_PARAM_BOOL(strict)
3322         ZEND_PARSE_PARAMETERS_END();
3323 #endif
3324 
3325         /* Initialize return array */
3326         if (search_value != NULL) {
3327                 array_init(return_value);
3328 
3329                 if (strict) {
3330                         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3331                                 ZVAL_DEREF(entry);
3332                                 if (fast_is_identical_function(search_value, entry)) {
3333                                         if (str_idx) {
3334                                                 ZVAL_STR_COPY(&new_val, str_idx);
3335                                         } else {
3336                                                 ZVAL_LONG(&new_val, num_idx);
3337                                         }
3338                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
3339                                 }
3340                         } ZEND_HASH_FOREACH_END();
3341                 } else {
3342                         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3343                                 if (fast_equal_check_function(search_value, entry)) {
3344                                         if (str_idx) {
3345                                                 ZVAL_STR_COPY(&new_val, str_idx);
3346                                         } else {
3347                                                 ZVAL_LONG(&new_val, num_idx);
3348                                         }
3349                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
3350                                 }
3351                         } ZEND_HASH_FOREACH_END();
3352                 }
3353         } else {
3354                 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
3355                 if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
3356                         return;
3357                 }
3358                 zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
3359                 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3360                         /* Go through input array and add keys to the return array */
3361                         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3362                                 if (str_idx) {
3363                                         ZVAL_STR_COPY(&new_val, str_idx);
3364                                 } else {
3365                                         ZVAL_LONG(&new_val, num_idx);
3366                                 }
3367                                 ZEND_HASH_FILL_ADD(&new_val);
3368                         } ZEND_HASH_FOREACH_END();
3369                 } ZEND_HASH_FILL_END();
3370         }
3371 }
3372 /* }}} */
3373 
3374 /* {{{ proto array array_values(array input)
3375    Return just the values from the input array */
3376 PHP_FUNCTION(array_values)
3377 {
3378         zval     *input,                /* Input array */
3379                          *entry;                /* An entry in the input array */
3380 
3381 #ifndef FAST_ZPP
3382         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
3383                 return;
3384         }
3385 #else
3386         ZEND_PARSE_PARAMETERS_START(1, 1)
3387                 Z_PARAM_ARRAY(input)
3388         ZEND_PARSE_PARAMETERS_END();
3389 #endif
3390 
3391         /* Initialize return array */
3392         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
3393 
3394         if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
3395                 return;
3396         }
3397 
3398         zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
3399 
3400         /* Go through input array and add values to the return array */
3401         ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3402                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
3403                         if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
3404                                 entry = Z_REFVAL_P(entry);
3405                         }
3406                         Z_TRY_ADDREF_P(entry);
3407                         ZEND_HASH_FILL_ADD(entry);
3408                 } ZEND_HASH_FOREACH_END();
3409         } ZEND_HASH_FILL_END();
3410 }
3411 /* }}} */
3412 
3413 /* {{{ proto array array_count_values(array input)
3414    Return the value as key and the frequency of that value in input as value */
3415 PHP_FUNCTION(array_count_values)
3416 {
3417         zval    *input,         /* Input array */
3418                         *entry,         /* An entry in the input array */
3419                         *tmp;
3420         HashTable *myht;
3421 
3422         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
3423                 return;
3424         }
3425 
3426         /* Initialize return array */
3427         array_init(return_value);
3428 
3429         /* Go through input array and add values to the return array */
3430         myht = Z_ARRVAL_P(input);
3431         ZEND_HASH_FOREACH_VAL(myht, entry) {
3432                 ZVAL_DEREF(entry);
3433                 if (Z_TYPE_P(entry) == IS_LONG) {
3434                         if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
3435                                 zval data;
3436                                 ZVAL_LONG(&data, 1);
3437                                 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
3438                         } else {
3439                                 Z_LVAL_P(tmp)++;
3440                         }
3441                 } else if (Z_TYPE_P(entry) == IS_STRING) {
3442                         if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
3443                                 zval data;
3444                                 ZVAL_LONG(&data, 1);
3445                                 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
3446                         } else {
3447                                 Z_LVAL_P(tmp)++;
3448                         }
3449                 } else {
3450                         php_error_docref(NULL, E_WARNING, "Can only count STRING and INTEGER values!");
3451                 }
3452         } ZEND_HASH_FOREACH_END();
3453 }
3454 /* }}} */
3455 
3456 /* {{{ array_column_param_helper
3457  * Specialized conversion rules for array_column() function
3458  */
3459 static inline
3460 zend_bool array_column_param_helper(zval *param,
3461                                     const char *name) {
3462         switch (Z_TYPE_P(param)) {
3463                 case IS_DOUBLE:
3464                         convert_to_long_ex(param);
3465                         /* fallthrough */
3466                 case IS_LONG:
3467                         return 1;
3468 
3469                 case IS_OBJECT:
3470                         convert_to_string_ex(param);
3471                         /* fallthrough */
3472                 case IS_STRING:
3473                         return 1;
3474 
3475                 default:
3476                         php_error_docref(NULL, E_WARNING, "The %s key should be either a string or an integer", name);
3477                         return 0;
3478         }
3479 }
3480 /* }}} */
3481 
3482 static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
3483 {
3484         zval *prop = NULL;
3485 
3486         if (Z_TYPE_P(data) == IS_OBJECT) {
3487                 zend_string *key = zval_get_string(name);
3488 
3489                 if (!Z_OBJ_HANDLER_P(data, has_property) || Z_OBJ_HANDLER_P(data, has_property)(data, name, 1, NULL)) {
3490                         prop = zend_read_property(Z_OBJCE_P(data), data, ZSTR_VAL(key), ZSTR_LEN(key), 1, rv);
3491                 }
3492                 zend_string_release(key);
3493         } else if (Z_TYPE_P(data) == IS_ARRAY) {
3494                 if (Z_TYPE_P(name) == IS_STRING) {
3495                         prop = zend_hash_find(Z_ARRVAL_P(data), Z_STR_P(name));
3496                 } else if (Z_TYPE_P(name) == IS_LONG) {
3497                         prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name));
3498                 }
3499         }
3500 
3501         if (prop) {
3502                 ZVAL_DEREF(prop);
3503         }
3504 
3505         return prop;
3506 }
3507 
3508 /* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
3509    Return the values from a single column in the input array, identified by the
3510    value_key and optionally indexed by the index_key */
3511 PHP_FUNCTION(array_column)
3512 {
3513         zval *zcolumn = NULL, *zkey = NULL, *data;
3514         HashTable *arr_hash;
3515         zval *zcolval = NULL, *zkeyval = NULL, rvc, rvk;
3516 
3517         if (zend_parse_parameters(ZEND_NUM_ARGS(), "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
3518                 return;
3519         }
3520 
3521         if ((zcolumn && !array_column_param_helper(zcolumn, "column")) ||
3522             (zkey && !array_column_param_helper(zkey, "index"))) {
3523                 RETURN_FALSE;
3524         }
3525 
3526         array_init(return_value);
3527         ZEND_HASH_FOREACH_VAL(arr_hash, data) {
3528                 ZVAL_DEREF(data);
3529 
3530                 if (!zcolumn) {
3531                         zcolval = data;
3532                 } else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
3533                         continue;
3534                 }
3535 
3536                 /* Failure will leave zkeyval alone which will land us on the final else block below
3537                  * which is to append the value as next_index
3538                  */
3539                 if (zkey) {
3540                         zkeyval = array_column_fetch_prop(data, zkey, &rvk);
3541                 }
3542 
3543                 Z_TRY_ADDREF_P(zcolval);
3544                 if (zkeyval && Z_TYPE_P(zkeyval) == IS_STRING) {
3545                         zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
3546                 } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_LONG) {
3547                         add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval);
3548                 } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_OBJECT) {
3549                         zend_string *key = zval_get_string(zkeyval);
3550                         zend_symtable_update(Z_ARRVAL_P(return_value), key, zcolval);
3551                         zend_string_release(key);
3552                 } else {
3553                         add_next_index_zval(return_value, zcolval);
3554                 }
3555                 if (zcolval == &rvc) {
3556                         zval_ptr_dtor(&rvc);
3557                 }
3558                 if (zkeyval == &rvk) {
3559                         zval_ptr_dtor(&rvk);
3560                 }
3561         } ZEND_HASH_FOREACH_END();
3562 }
3563 /* }}} */
3564 
3565 /* {{{ proto array array_reverse(array input [, bool preserve keys])
3566    Return input as a new array with the order of the entries reversed */
3567 PHP_FUNCTION(array_reverse)
3568 {
3569         zval     *input,                                /* Input array */
3570                          *entry;                                /* An entry in the input array */
3571         zend_string *string_key;
3572         zend_ulong        num_key;
3573         zend_bool preserve_keys = 0;    /* whether to preserve keys */
3574 
3575         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &input, &preserve_keys) == FAILURE) {
3576                 return;
3577         }
3578 
3579         /* Initialize return array */
3580         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
3581 
3582         ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
3583                 if (string_key) {
3584                         entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3585                 } else {
3586                         if (preserve_keys) {
3587                                 entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3588                         } else {
3589                                 entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3590                         }
3591                 }
3592 
3593                 zval_add_ref(entry);
3594         } ZEND_HASH_FOREACH_END();
3595 }
3596 /* }}} */
3597 
3598 /* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
3599    Returns a copy of input array padded with pad_value to size pad_size */
3600 PHP_FUNCTION(array_pad)
3601 {
3602         zval  *input;           /* Input array */
3603         zval  *pad_value;       /* Padding value obviously */
3604         zend_long pad_size;             /* Size to pad to */
3605         zend_long pad_size_abs; /* Absolute value of pad_size */
3606         zend_long input_size;           /* Size of the input array */
3607         zend_long num_pads;             /* How many pads do we need */
3608         zend_long i;
3609         zend_string *key;
3610         zval *value;
3611 
3612         if (zend_parse_parameters(ZEND_NUM_ARGS(), "alz", &input, &pad_size, &pad_value) == FAILURE) {
3613                 return;
3614         }
3615 
3616         /* Do some initial calculations */
3617         input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
3618         pad_size_abs = ZEND_ABS(pad_size);
3619         if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) {
3620                 php_error_docref(NULL, E_WARNING, "You may only pad up to 1048576 elements at a time");
3621                 RETURN_FALSE;
3622         }
3623 
3624         if (input_size >= pad_size_abs) {
3625                 /* Copy the original array */
3626                 ZVAL_COPY(return_value, input);
3627                 return;
3628         }
3629 
3630         num_pads = pad_size_abs - input_size;
3631         array_init_size(return_value, pad_size_abs);
3632         if (Z_REFCOUNTED_P(pad_value)) {
3633                 GC_REFCOUNT(Z_COUNTED_P(pad_value)) += num_pads;
3634         }
3635 
3636         if (pad_size < 0) {
3637                 for (i = 0; i < num_pads; i++) {
3638                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
3639                 }
3640         }
3641 
3642         ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) {
3643                 if (Z_REFCOUNTED_P(value)) {
3644                         Z_ADDREF_P(value);
3645                 }
3646                 if (key) {
3647                         zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
3648                 } else {
3649                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
3650                 }
3651         } ZEND_HASH_FOREACH_END();
3652 
3653         if (pad_size > 0) {
3654                 for (i = 0; i < num_pads; i++) {
3655                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
3656                 }
3657         }
3658 }
3659 /* }}} */
3660 
3661 /* {{{ proto array array_flip(array input)
3662    Return array with key <-> value flipped */
3663 PHP_FUNCTION(array_flip)
3664 {
3665         zval *array, *entry, data;
3666         zend_ulong num_idx;
3667         zend_string *str_idx;
3668 
3669         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
3670                 return;
3671         }
3672 
3673         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
3674 
3675         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
3676                 ZVAL_DEREF(entry);
3677                 if (Z_TYPE_P(entry) == IS_LONG) {
3678                         if (str_idx) {
3679                                 ZVAL_STR_COPY(&data, str_idx);
3680                         } else {
3681                                 ZVAL_LONG(&data, num_idx);
3682                         }
3683                         zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
3684                 } else if (Z_TYPE_P(entry) == IS_STRING) {
3685                         if (str_idx) {
3686                                 ZVAL_STR_COPY(&data, str_idx);
3687                         } else {
3688                                 ZVAL_LONG(&data, num_idx);
3689                         }
3690                         zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
3691                 } else {
3692                         php_error_docref(NULL, E_WARNING, "Can only flip STRING and INTEGER values!");
3693                 }
3694         } ZEND_HASH_FOREACH_END();
3695 }
3696 /* }}} */
3697 
3698 /* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
3699    Retuns an array with all string keys lowercased [or uppercased] */
3700 PHP_FUNCTION(array_change_key_case)
3701 {
3702         zval *array, *entry;
3703         zend_string *string_key;
3704         zend_string *new_key;
3705         zend_ulong num_key;
3706         zend_long change_to_upper=0;
3707 
3708         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &change_to_upper) == FAILURE) {
3709                 return;
3710         }
3711 
3712         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
3713 
3714         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
3715                 if (!string_key) {
3716                         entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
3717                 } else {
3718                         if (change_to_upper) {
3719                                 new_key = php_string_toupper(string_key);
3720                         } else {
3721                                 new_key = php_string_tolower(string_key);
3722                         }
3723                         entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
3724                         zend_string_release(new_key);
3725                 }
3726 
3727                 zval_add_ref(entry);
3728         } ZEND_HASH_FOREACH_END();
3729 }
3730 /* }}} */
3731 
3732 struct bucketindex {
3733         Bucket b;
3734         unsigned int i;
3735 };
3736 
3737 static void array_bucketindex_swap(void *p, void *q) /* {{{ */
3738 {
3739         struct bucketindex *f = (struct bucketindex *)p;
3740         struct bucketindex *g = (struct bucketindex *)q;
3741         struct bucketindex t;
3742         t = *f;
3743         *f = *g;
3744         *g = t;
3745 }
3746 /* }}} */
3747 
3748 /* {{{ proto array array_unique(array input [, int sort_flags])
3749    Removes duplicate values from array */
3750 PHP_FUNCTION(array_unique)
3751 {
3752         zval *array;
3753         uint idx;
3754         Bucket *p;
3755         struct bucketindex *arTmp, *cmpdata, *lastkept;
3756         unsigned int i;
3757         zend_long sort_type = PHP_SORT_STRING;
3758         compare_func_t cmp;
3759 
3760         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &sort_type) == FAILURE) {
3761                 return;
3762         }
3763 
3764         cmp = php_get_data_compare_func(sort_type, 0);
3765 
3766 
3767         if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {   /* nothing to do */
3768                 ZVAL_COPY(return_value, array);
3769                 return;
3770         }
3771 
3772         RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
3773 
3774         /* create and sort array with pointers to the target_hash buckets */
3775         arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
3776         if (!arTmp) {
3777                 zval_dtor(return_value);
3778                 RETURN_FALSE;
3779         }
3780         for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) {
3781                 p = Z_ARRVAL_P(array)->arData + idx;
3782                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
3783                 if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue;
3784                 arTmp[i].b = *p;
3785                 arTmp[i].i = i;
3786                 i++;
3787         }
3788         ZVAL_UNDEF(&arTmp[i].b.val);
3789         zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
3790                         cmp, (swap_func_t)array_bucketindex_swap);
3791         /* go through the sorted array and delete duplicates from the copy */
3792         lastkept = arTmp;
3793         for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
3794                 if (cmp(lastkept, cmpdata)) {
3795                         lastkept = cmpdata;
3796                 } else {
3797                         if (lastkept->i > cmpdata->i) {
3798                                 p = &lastkept->b;
3799                                 lastkept = cmpdata;
3800                         } else {
3801                                 p = &cmpdata->b;
3802                         }
3803                         if (p->key == NULL) {
3804                                 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3805                         } else {
3806                                 if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
3807                                         zend_delete_global_variable(p->key);
3808                                 } else {
3809                                         zend_hash_del(Z_ARRVAL_P(return_value), p->key);
3810                                 }
3811                         }
3812                 }
3813         }
3814         pefree(arTmp, Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
3815 }
3816 /* }}} */
3817 
3818 static int zval_compare(zval *first, zval *second) /* {{{ */
3819 {
3820         return string_compare_function(first, second);
3821 }
3822 /* }}} */
3823 
3824 static int zval_user_compare(zval *a, zval *b) /* {{{ */
3825 {
3826         zval args[2];
3827         zval retval;
3828 
3829         ZVAL_COPY_VALUE(&args[0], a);
3830         ZVAL_COPY_VALUE(&args[1], b);
3831 
3832         BG(user_compare_fci).param_count = 2;
3833         BG(user_compare_fci).params = args;
3834         BG(user_compare_fci).retval = &retval;
3835         BG(user_compare_fci).no_separation = 0;
3836 
3837         if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
3838                 zend_long ret = zval_get_long(&retval);
3839                 zval_ptr_dtor(&retval);
3840                 return ret < 0 ? -1 : ret > 0 ? 1 : 0;;
3841         } else {
3842                 return 0;
3843         }
3844 }
3845 /* }}} */
3846 
3847 static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
3848 {
3849     uint idx;
3850         Bucket *p;
3851         int argc, i;
3852         zval *args;
3853         int (*intersect_data_compare_func)(zval *, zval *) = NULL;
3854         zend_bool ok;
3855         zval *val, *data;
3856         int req_args;
3857         char *param_spec;
3858 
3859         /* Get the argument count */
3860         argc = ZEND_NUM_ARGS();
3861         if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3862                 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
3863                 req_args = 3;
3864                 param_spec = "+f";
3865                 intersect_data_compare_func = zval_user_compare;
3866         } else {
3867                 /*      INTERSECT_COMP_DATA_NONE - array_intersect_key()
3868                         INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
3869                 req_args = 2;
3870                 param_spec = "+";
3871 
3872                 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
3873                         intersect_data_compare_func = zval_compare;
3874                 }
3875         }
3876 
3877         if (argc < req_args) {
3878                 php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
3879                 return;
3880         }
3881 
3882         if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
3883                 return;
3884         }
3885 
3886         for (i = 0; i < argc; i++) {
3887                 if (Z_TYPE(args[i]) != IS_ARRAY) {
3888                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
3889                         RETURN_NULL();
3890                 }
3891         }
3892 
3893         array_init(return_value);
3894 
3895         for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
3896                 p = Z_ARRVAL(args[0])->arData + idx;
3897                 val = &p->val;
3898                 if (Z_TYPE_P(val) == IS_UNDEF) continue;
3899                 if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
3900                         val = Z_INDIRECT_P(val);
3901                         if (Z_TYPE_P(val) == IS_UNDEF) continue;
3902                 }
3903                 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
3904                         ZVAL_UNREF(val);
3905                 }
3906                 if (p->key == NULL) {
3907                         ok = 1;
3908                         for (i = 1; i < argc; i++) {
3909                                 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) == NULL ||
3910                                         (intersect_data_compare_func &&
3911                                         intersect_data_compare_func(val, data) != 0)
3912                                 ) {
3913                                         ok = 0;
3914                                         break;
3915                                 }
3916                         }
3917                         if (ok) {
3918                                 if (Z_REFCOUNTED_P(val)) {
3919                                         Z_ADDREF_P(val);
3920                                 }
3921                                 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
3922                         }
3923                 } else {
3924                         ok = 1;
3925                         for (i = 1; i < argc; i++) {
3926                                 if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) == NULL ||
3927                                         (intersect_data_compare_func &&
3928                                         intersect_data_compare_func(val, data) != 0)
3929                                 ) {
3930                                         ok = 0;
3931                                         break;
3932                                 }
3933                         }
3934                         if (ok) {
3935                                 if (Z_REFCOUNTED_P(val)) {
3936                                         Z_ADDREF_P(val);
3937                                 }
3938                                 zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
3939                         }
3940                 }
3941         }
3942 }
3943 /* }}} */
3944 
3945 static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
3946 {
3947         zval *args = NULL;
3948         HashTable *hash;
3949         int arr_argc, i, c = 0;
3950         uint idx;
3951         Bucket **lists, *list, **ptrs, *p;
3952         uint32_t req_args;
3953         char *param_spec;
3954         zend_fcall_info fci1, fci2;
3955         zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
3956         zend_fcall_info *fci_key = NULL, *fci_data;
3957         zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
3958         PHP_ARRAY_CMP_FUNC_VARS;
3959 
3960         int (*intersect_key_compare_func)(const void *, const void *);
3961         int (*intersect_data_compare_func)(const void *, const void *);
3962 
3963         if (behavior == INTERSECT_NORMAL) {
3964                 intersect_key_compare_func = php_array_key_compare_string;
3965 
3966                 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
3967                         /* array_intersect() */
3968                         req_args = 2;
3969                         param_spec = "+";
3970                         intersect_data_compare_func = php_array_data_compare_string;
3971                 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3972                         /* array_uintersect() */
3973                         req_args = 3;
3974                         param_spec = "+f";
3975                         intersect_data_compare_func = php_array_user_compare;
3976                 } else {
3977                         php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
3978                         return;
3979                 }
3980 
3981                 if (ZEND_NUM_ARGS() < req_args) {
3982                         php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3983                         return;
3984                 }
3985 
3986                 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3987                         return;
3988                 }
3989                 fci_data = &fci1;
3990                 fci_data_cache = &fci1_cache;
3991 
3992         } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3993                 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
3994                  * no comparison of the data is done (part of INTERSECT_ASSOC) */
3995                 intersect_key_compare_func = php_array_key_compare_string;
3996 
3997                 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
3998                         /* array_intersect_assoc() or array_intersect_key() */
3999                         req_args = 2;
4000                         param_spec = "+";
4001                         intersect_key_compare_func = php_array_key_compare_string;
4002                         intersect_data_compare_func = php_array_data_compare_string;
4003                 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
4004                         /* array_uintersect_assoc() */
4005                         req_args = 3;
4006                         param_spec = "+f";
4007                         intersect_key_compare_func = php_array_key_compare_string;
4008                         intersect_data_compare_func = php_array_user_compare;
4009                         fci_data = &fci1;
4010                         fci_data_cache = &fci1_cache;
4011                 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
4012                         /* array_intersect_uassoc() or array_intersect_ukey() */
4013                         req_args = 3;
4014                         param_spec = "+f";
4015                         intersect_key_compare_func = php_array_user_key_compare;
4016                         intersect_data_compare_func = php_array_data_compare_string;
4017                         fci_key = &fci1;
4018                         fci_key_cache = &fci1_cache;
4019                 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
4020                         /* array_uintersect_uassoc() */
4021                         req_args = 4;
4022                         param_spec = "+ff";
4023                         intersect_key_compare_func = php_array_user_key_compare;
4024                         intersect_data_compare_func = php_array_user_compare;
4025                         fci_data = &fci1;
4026                         fci_data_cache = &fci1_cache;
4027                         fci_key = &fci2;
4028                         fci_key_cache = &fci2_cache;
4029                 } else {
4030                         php_error_docref(NULL, 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);
4031                         return;
4032                 }
4033 
4034                 if (ZEND_NUM_ARGS() < req_args) {
4035                         php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
4036                         return;
4037                 }
4038 
4039                 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
4040                         return;
4041                 }
4042 
4043         } else {
4044                 php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
4045                 return;
4046         }
4047 
4048         PHP_ARRAY_CMP_FUNC_BACKUP();
4049 
4050         /* for each argument, create and sort list with pointers to the hash buckets */
4051         lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4052         ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4053 
4054         if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
4055                 BG(user_compare_fci) = *fci_data;
4056                 BG(user_compare_fci_cache) = *fci_data_cache;
4057         } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
4058                 BG(user_compare_fci) = *fci_key;
4059                 BG(user_compare_fci_cache) = *fci_key_cache;
4060         }
4061 
4062         for (i = 0; i < arr_argc; i++) {
4063                 if (Z_TYPE(args[i]) != IS_ARRAY) {
4064                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
4065                         arr_argc = i; /* only free up to i - 1 */
4066                         goto out;
4067                 }
4068                 hash = Z_ARRVAL(args[i]);
4069                 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
4070                 if (!list) {
4071                         PHP_ARRAY_CMP_FUNC_RESTORE();
4072 
4073                         efree(ptrs);
4074                         efree(lists);
4075                         RETURN_FALSE;
4076                 }
4077                 lists[i] = list;
4078                 ptrs[i] = list;
4079                 for (idx = 0; idx < hash->nNumUsed; idx++) {
4080                         p = hash->arData + idx;
4081                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
4082                         *list++ = *p;
4083                 }
4084                 ZVAL_UNDEF(&list->val);
4085                 if (hash->nNumOfElements > 1) {
4086                         if (behavior == INTERSECT_NORMAL) {
4087                                 zend_sort((void *) lists[i], hash->nNumOfElements, 
4088                                                 sizeof(Bucket), intersect_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
4089                         } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4090                                 zend_sort((void *) lists[i], hash->nNumOfElements,
4091                                                 sizeof(Bucket), intersect_key_compare_func, (swap_func_t)zend_hash_bucket_swap);
4092                         }
4093                 }
4094         }
4095 
4096         /* copy the argument array */
4097         RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
4098 
4099         /* go through the lists and look for common values */
4100         while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
4101                 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
4102                         && key_compare_type == INTERSECT_COMP_KEY_USER) {
4103                         BG(user_compare_fci) = *fci_key;
4104                         BG(user_compare_fci_cache) = *fci_key_cache;
4105                 }
4106 
4107                 for (i = 1; i < arr_argc; i++) {
4108                         if (behavior & INTERSECT_NORMAL) {
4109                                 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
4110                                         ptrs[i]++;
4111                                 }
4112                         } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4113                                 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
4114                                         ptrs[i]++;
4115                                 }
4116                                 if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
4117                                         /* this means that ptrs[i] is not NULL so we can compare
4118                                          * and "c==0" is from last operation
4119                                          * in this branch of code we enter only when INTERSECT_ASSOC
4120                                          * since when we have INTERSECT_KEY compare of data is not wanted. */
4121                                         if (data_compare_type == INTERSECT_COMP_DATA_USER) {
4122                                                 BG(user_compare_fci) = *fci_data;
4123                                                 BG(user_compare_fci_cache) = *fci_data_cache;
4124                                         }
4125                                         if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
4126                                                 c = 1;
4127                                                 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
4128                                                         BG(user_compare_fci) = *fci_key;
4129                                                         BG(user_compare_fci_cache) = *fci_key_cache;
4130                                                         /* When KEY_USER, the last parameter is always the callback */
4131                                                 }
4132                                                 /* we are going to the break */
4133                                         } else {
4134                                                 /* continue looping */
4135                                         }
4136                                 }
4137                         }
4138                         if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
4139                                 /* delete any values corresponding to remains of ptrs[0] */
4140                                 /* and exit because they do not present in at least one of */
4141                                 /* the other arguments */
4142                                 for (;;) {
4143                                         p = ptrs[0]++;
4144                                         if (Z_TYPE(p->val) == IS_UNDEF) {
4145                                                 goto out;
4146                                         }
4147                                         if (p->key == NULL) {
4148                                                 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4149                                         } else {
4150                                                 zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4151                                         }
4152                                 }
4153                         }
4154                         if (c) /* here we get if not all are equal */
4155                                 break;
4156                         ptrs[i]++;
4157                 }
4158                 if (c) {
4159                         /* Value of ptrs[0] not in all arguments, delete all entries */
4160                         /* with value < value of ptrs[i] */
4161                         for (;;) {
4162                                 p = ptrs[0];
4163                                 if (p->key == NULL) {
4164                                         zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4165                                 } else {
4166                                         zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4167                                 }
4168                                 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4169                                         goto out;
4170                                 }
4171                                 if (behavior == INTERSECT_NORMAL) {
4172                                         if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
4173                                                 break;
4174                                         }
4175                                 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4176                                         /* no need of looping because indexes are unique */
4177                                         break;
4178                                 }
4179                         }
4180                 } else {
4181                         /* ptrs[0] is present in all the arguments */
4182                         /* Skip all entries with same value as ptrs[0] */
4183                         for (;;) {
4184                                 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4185                                         goto out;
4186                                 }
4187                                 if (behavior == INTERSECT_NORMAL) {
4188                                         if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
4189                                                 break;
4190                                         }
4191                                 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
4192                                         /* no need of looping because indexes are unique */
4193                                         break;
4194                                 }
4195                         }
4196                 }
4197         }
4198 out:
4199         for (i = 0; i < arr_argc; i++) {
4200                 hash = Z_ARRVAL(args[i]);
4201                 pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
4202         }
4203 
4204         PHP_ARRAY_CMP_FUNC_RESTORE();
4205 
4206         efree(ptrs);
4207         efree(lists);
4208 }
4209 /* }}} */
4210 
4211 /* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
4212    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. */
4213 PHP_FUNCTION(array_intersect_key)
4214 {
4215         php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
4216 }
4217 /* }}} */
4218 
4219 /* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
4220    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. */
4221 PHP_FUNCTION(array_intersect_ukey)
4222 {
4223         php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
4224 }
4225 /* }}} */
4226 
4227 /* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
4228    Returns the entries of arr1 that have values which are present in all the other arguments */
4229 PHP_FUNCTION(array_intersect)
4230 {
4231         php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
4232 }
4233 /* }}} */
4234 
4235 /* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
4236    Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */
4237 PHP_FUNCTION(array_uintersect)
4238 {
4239         php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
4240 }
4241 /* }}} */
4242 
4243 /* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
4244    Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
4245 PHP_FUNCTION(array_intersect_assoc)
4246 {
4247         php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
4248 }
4249 /* }}} */
4250 
4251 /* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
4252    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 a user-supplied callback. */
4253 PHP_FUNCTION(array_intersect_uassoc)
4254 {
4255         php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
4256 }
4257 /* }}} */
4258 
4259 /* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
4260    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 a user-supplied callback. */
4261 PHP_FUNCTION(array_uintersect_assoc)
4262 {
4263         php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
4264 }
4265 /* }}} */
4266 
4267 /* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
4268    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. */
4269 PHP_FUNCTION(array_uintersect_uassoc)
4270 {
4271         php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
4272 }
4273 /* }}} */
4274 
4275 static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4276 {
4277     uint idx;
4278         Bucket *p;
4279         int argc, i;
4280         zval *args;
4281         int (*diff_data_compare_func)(zval *, zval *) = NULL;
4282         zend_bool ok;
4283         zval *val, *data;
4284 
4285         /* Get the argument count */
4286         argc = ZEND_NUM_ARGS();
4287         if (data_compare_type == DIFF_COMP_DATA_USER) {
4288                 if (argc < 3) {
4289                         php_error_docref(NULL, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
4290                         return;
4291                 }
4292                 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
4293                         return;
4294                 }
4295                 diff_data_compare_func = zval_user_compare;
4296         } else {
4297                 if (argc < 2) {
4298                         php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
4299                         return;
4300                 }
4301                 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
4302                         return;
4303                 }
4304                 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
4305                         diff_data_compare_func = zval_compare;
4306                 }
4307         }
4308 
4309         for (i = 0; i < argc; i++) {
4310                 if (Z_TYPE(args[i]) != IS_ARRAY) {
4311                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
4312                         RETURN_NULL();
4313                 }
4314         }
4315 
4316         array_init(return_value);
4317 
4318         for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
4319                 p = Z_ARRVAL(args[0])->arData + idx;
4320                 val = &p->val;
4321                 if (Z_TYPE_P(val) == IS_UNDEF) continue;
4322                 if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
4323                         val = Z_INDIRECT_P(val);
4324                         if (Z_TYPE_P(val) == IS_UNDEF) continue;
4325                 }
4326                 if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
4327                         ZVAL_UNREF(val);
4328                 }
4329                 if (p->key == NULL) {
4330                         ok = 1;
4331                         for (i = 1; i < argc; i++) {
4332                                 if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) != NULL &&
4333                                         (!diff_data_compare_func ||
4334                                         diff_data_compare_func(val, data) == 0)
4335                                 ) {
4336                                         ok = 0;
4337                                         break;
4338                                 }
4339                         }
4340                         if (ok) {
4341                                 if (Z_REFCOUNTED_P(val)) {
4342                                         Z_ADDREF_P(val);
4343                                 }
4344                                 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
4345                         }
4346                 } else {
4347                         ok = 1;
4348                         for (i = 1; i < argc; i++) {
4349                                 if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) != NULL &&
4350                                         (!diff_data_compare_func ||
4351                                         diff_data_compare_func(val, data) == 0)
4352                                 ) {
4353                                         ok = 0;
4354                                         break;
4355                                 }
4356                         }
4357                         if (ok) {
4358                                 if (Z_REFCOUNTED_P(val)) {
4359                                         Z_ADDREF_P(val);
4360                                 }
4361                                 zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
4362                         }
4363                 }
4364         }
4365 }
4366 /* }}} */
4367 
4368 static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
4369 {
4370         zval *args = NULL;
4371         HashTable *hash;
4372         int arr_argc, i, c;
4373         uint idx;
4374         Bucket **lists, *list, **ptrs, *p;
4375         uint32_t req_args;
4376         char *param_spec;
4377         zend_fcall_info fci1, fci2;
4378         zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
4379         zend_fcall_info *fci_key = NULL, *fci_data;
4380         zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
4381         PHP_ARRAY_CMP_FUNC_VARS;
4382 
4383         int (*diff_key_compare_func)(const void *, const void *);
4384         int (*diff_data_compare_func)(const void *, const void *);
4385 
4386         if (behavior == DIFF_NORMAL) {
4387                 diff_key_compare_func = php_array_key_compare_string;
4388 
4389                 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
4390                         /* array_diff */
4391                         req_args = 2;
4392                         param_spec = "+";
4393                         diff_data_compare_func = php_array_data_compare_string;
4394                 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
4395                         /* array_udiff */
4396                         req_args = 3;
4397                         param_spec = "+f";
4398                         diff_data_compare_func = php_array_user_compare;
4399                 } else {
4400                         php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
4401                         return;
4402                 }
4403 
4404                 if (ZEND_NUM_ARGS() < req_args) {
4405                         php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
4406                         return;
4407                 }
4408 
4409                 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
4410                         return;
4411                 }
4412                 fci_data = &fci1;
4413                 fci_data_cache = &fci1_cache;
4414 
4415         } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
4416                 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
4417                  * no comparison of the data is done (part of DIFF_ASSOC) */
4418 
4419                 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
4420                         /* array_diff_assoc() or array_diff_key() */
4421                         req_args = 2;
4422                         param_spec = "+";
4423                         diff_key_compare_func = php_array_key_compare_string;
4424                         diff_data_compare_func = php_array_data_compare_string;
4425                 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
4426                         /* array_udiff_assoc() */
4427                         req_args = 3;
4428                         param_spec = "+f";
4429                         diff_key_compare_func = php_array_key_compare_string;
4430                         diff_data_compare_func = php_array_user_compare;
4431                         fci_data = &fci1;
4432                         fci_data_cache = &fci1_cache;
4433                 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
4434                         /* array_diff_uassoc() or array_diff_ukey() */
4435                         req_args = 3;
4436                         param_spec = "+f";
4437                         diff_key_compare_func = php_array_user_key_compare;
4438                         diff_data_compare_func = php_array_data_compare_string;
4439                         fci_key = &fci1;
4440                         fci_key_cache = &fci1_cache;
4441                 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
4442                         /* array_udiff_uassoc() */
4443                         req_args = 4;
4444                         param_spec = "+ff";
4445                         diff_key_compare_func = php_array_user_key_compare;
4446                         diff_data_compare_func = php_array_user_compare;
4447                         fci_data = &fci1;
4448                         fci_data_cache = &fci1_cache;
4449                         fci_key = &fci2;
4450                         fci_key_cache = &fci2_cache;
4451                 } else {
4452                         php_error_docref(NULL, 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);
4453                         return;
4454                 }
4455 
4456                 if (ZEND_NUM_ARGS() < req_args) {
4457                         php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
4458                         return;
4459                 }
4460 
4461                 if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
4462                         return;
4463                 }
4464 
4465         } else {
4466                 php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
4467                 return;
4468         }
4469 
4470         PHP_ARRAY_CMP_FUNC_BACKUP();
4471 
4472         /* for each argument, create and sort list with pointers to the hash buckets */
4473         lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4474         ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
4475 
4476         if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
4477                 BG(user_compare_fci) = *fci_data;
4478                 BG(user_compare_fci_cache) = *fci_data_cache;
4479         } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
4480                 BG(user_compare_fci) = *fci_key;
4481                 BG(user_compare_fci_cache) = *fci_key_cache;
4482         }
4483 
4484         for (i = 0; i < arr_argc; i++) {
4485                 if (Z_TYPE(args[i]) != IS_ARRAY) {
4486                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
4487                         arr_argc = i; /* only free up to i - 1 */
4488                         goto out;
4489                 }
4490                 hash = Z_ARRVAL(args[i]);
4491                 list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
4492                 if (!list) {
4493                         PHP_ARRAY_CMP_FUNC_RESTORE();
4494 
4495                         efree(ptrs);
4496                         efree(lists);
4497                         RETURN_FALSE;
4498                 }
4499                 lists[i] = list;
4500                 ptrs[i] = list;
4501                 for (idx = 0; idx < hash->nNumUsed; idx++) {
4502                         p = hash->arData + idx;
4503                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
4504                         *list++ = *p;
4505                 }
4506                 ZVAL_UNDEF(&list->val);
4507                 if (hash->nNumOfElements > 1) {
4508                         if (behavior == DIFF_NORMAL) {
4509                                 zend_sort((void *) lists[i], hash->nNumOfElements,
4510                                                 sizeof(Bucket), diff_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
4511                         } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
4512                                 zend_sort((void *) lists[i], hash->nNumOfElements,
4513                                                 sizeof(Bucket), diff_key_compare_func, (swap_func_t)zend_hash_bucket_swap);
4514                         }
4515                 }
4516         }
4517 
4518         /* copy the argument array */
4519         RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
4520 
4521         /* go through the lists and look for values of ptr[0] that are not in the others */
4522         while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
4523                 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
4524                         &&
4525                         key_compare_type == DIFF_COMP_KEY_USER
4526                 ) {
4527                         BG(user_compare_fci) = *fci_key;
4528                         BG(user_compare_fci_cache) = *fci_key_cache;
4529                 }
4530                 c = 1;
4531                 for (i = 1; i < arr_argc; i++) {
4532                         Bucket *ptr = ptrs[i];
4533                         if (behavior == DIFF_NORMAL) {
4534                                 while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
4535                                         ptrs[i]++;
4536                                 }
4537                         } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
4538                                 while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
4539                                         ptr++;
4540                                 }
4541                         }
4542                         if (!c) {
4543                                 if (behavior == DIFF_NORMAL) {
4544                                         if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
4545                                                 ptrs[i]++;
4546                                         }
4547                                         break;
4548                                 } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
4549                                         /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
4550                                          * data comparison is not needed - skipped. */
4551                                         if (Z_TYPE(ptr->val) != IS_UNDEF) {
4552                                                 if (data_compare_type == DIFF_COMP_DATA_USER) {
4553                                                         BG(user_compare_fci) = *fci_data;
4554                                                         BG(user_compare_fci_cache) = *fci_data_cache;
4555                                                 }
4556                                                 if (diff_data_compare_func(ptrs[0], ptr) != 0) {
4557                                                         /* the data is not the same */
4558                                                         c = -1;
4559                                                         if (key_compare_type == DIFF_COMP_KEY_USER) {
4560                                                                 BG(user_compare_fci) = *fci_key;
4561                                                                 BG(user_compare_fci_cache) = *fci_key_cache;
4562                                                         }
4563                                                 } else {
4564                                                         break;
4565                                                         /* we have found the element in other arrays thus we don't want it
4566                                                          * in the return_value -> delete from there */
4567                                                 }
4568                                         }
4569                                 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
4570                                         /* the behavior here differs from INTERSECT_KEY in php_intersect
4571                                          * since in the "diff" case we have to remove the entry from
4572                                          * return_value while when doing intersection the entry must not
4573                                          * be deleted. */
4574                                         break; /* remove the key */
4575                                 }
4576                         }
4577                 }
4578                 if (!c) {
4579                         /* ptrs[0] in one of the other arguments */
4580                         /* delete all entries with value as ptrs[0] */
4581                         for (;;) {
4582                                 p = ptrs[0];
4583                                 if (p->key == NULL) {
4584                                         zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4585                                 } else {
4586                                         zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4587                                 }
4588                                 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4589                                         goto out;
4590                                 }
4591                                 if (behavior == DIFF_NORMAL) {
4592                                         if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
4593                                                 break;
4594                                         }
4595                                 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
4596                                         /* in this case no array_key_compare is needed */
4597                                         break;
4598                                 }
4599                         }
4600                 } else {
4601                         /* ptrs[0] in none of the other arguments */
4602                         /* skip all entries with value as ptrs[0] */
4603                         for (;;) {
4604                                 if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
4605                                         goto out;
4606                                 }
4607                                 if (behavior == DIFF_NORMAL) {
4608                                         if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
4609                                                 break;
4610                                         }
4611                                 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
4612                                         /* in this case no array_key_compare is needed */
4613                                         break;
4614                                 }
4615                         }
4616                 }
4617         }
4618 out:
4619         for (i = 0; i < arr_argc; i++) {
4620                 hash = Z_ARRVAL(args[i]);
4621                 pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
4622         }
4623 
4624         PHP_ARRAY_CMP_FUNC_RESTORE();
4625 
4626         efree(ptrs);
4627         efree(lists);
4628 }
4629 /* }}} */
4630 
4631 /* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
4632    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. */
4633 PHP_FUNCTION(array_diff_key)
4634 {
4635         php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
4636 }
4637 /* }}} */
4638 
4639 /* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
4640    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. */
4641 PHP_FUNCTION(array_diff_ukey)
4642 {
4643         php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
4644 }
4645 /* }}} */
4646 
4647 /* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
4648    Returns the entries of arr1 that have values which are not present in any of the others arguments. */
4649 PHP_FUNCTION(array_diff)
4650 {
4651         zval *args;
4652         int argc, i;
4653         uint32_t num;
4654         HashTable exclude;
4655         zval *value;
4656         zend_string *str, *key;
4657         zend_long idx;
4658         zval dummy;
4659 
4660         if (ZEND_NUM_ARGS() < 2) {
4661                 php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
4662                 return;
4663         }
4664 
4665         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
4666                 return;
4667         }
4668 
4669         if (Z_TYPE(args[0]) != IS_ARRAY) {
4670                 php_error_docref(NULL, E_WARNING, "Argument #1 is not an array");
4671                 RETURN_NULL();
4672         }
4673 
4674         /* count number of elements */
4675         num = 0;
4676         for (i = 1; i < argc; i++) {
4677                 if (Z_TYPE(args[i]) != IS_ARRAY) {
4678                         php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
4679                         RETURN_NULL();
4680                 }
4681                 num += zend_hash_num_elements(Z_ARRVAL(args[i]));
4682         }
4683 
4684         if (num == 0) {
4685                 ZVAL_COPY(return_value, &args[0]);
4686                 return;
4687         }
4688 
4689         ZVAL_NULL(&dummy);
4690         /* create exclude map */
4691         zend_hash_init(&exclude, num, NULL, NULL, 0);
4692         for (i = 1; i < argc; i++) {
4693                 ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), value) {
4694                         str = zval_get_string(value);
4695                         zend_hash_add(&exclude, str, &dummy);
4696                         zend_string_release(str);
4697                 } ZEND_HASH_FOREACH_END();
4698         }
4699 
4700         /* copy all elements of first array that are not in exclude set */
4701         array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
4702         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), idx, key, value) {
4703                 str = zval_get_string(value);
4704                 if (!zend_hash_exists(&exclude, str)) {
4705                         if (key) {
4706                                 value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4707                         } else {
4708                                 value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
4709                         }
4710                         zval_add_ref(value);
4711                 }
4712                 zend_string_release(str);
4713         } ZEND_HASH_FOREACH_END();
4714 
4715         zend_hash_destroy(&exclude);
4716 }
4717 /* }}} */
4718 
4719 /* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
4720    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. */
4721 PHP_FUNCTION(array_udiff)
4722 {
4723         php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
4724 }
4725 /* }}} */
4726 
4727 /* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
4728    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 */
4729 PHP_FUNCTION(array_diff_assoc)
4730 {
4731         php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
4732 }
4733 /* }}} */
4734 
4735 /* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
4736    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. */
4737 PHP_FUNCTION(array_diff_uassoc)
4738 {
4739         php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
4740 }
4741 /* }}} */
4742 
4743 /* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
4744    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. */
4745 PHP_FUNCTION(array_udiff_assoc)
4746 {
4747         php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
4748 }
4749 /* }}} */
4750 
4751 /* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
4752    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. */
4753 PHP_FUNCTION(array_udiff_uassoc)
4754 {
4755         php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
4756 }
4757 /* }}} */
4758 
4759 #define MULTISORT_ORDER 0
4760 #define MULTISORT_TYPE  1
4761 #define MULTISORT_LAST  2
4762 
4763 PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
4764 {
4765         Bucket *ab = *(Bucket **)a;
4766         Bucket *bb = *(Bucket **)b;
4767         int r;
4768         zend_long result;
4769 
4770         r = 0;
4771         do {
4772                 result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
4773                 if (result != 0) {
4774                         return result > 0 ? 1 : -1;
4775                 }
4776                 r++;
4777         } while (Z_TYPE(ab[r].val) != IS_UNDEF);
4778 
4779         return 0;
4780 }
4781 /* }}} */
4782 
4783 #define MULTISORT_ABORT                         \
4784         efree(ARRAYG(multisort_func));  \
4785         efree(arrays);                                  \
4786         RETURN_FALSE;
4787 
4788 static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
4789         Bucket *t;
4790         Bucket **f = (Bucket **)p;
4791         Bucket **g = (Bucket **)q;
4792 
4793         t = *f;
4794         *f = *g;
4795         *g = t;
4796 }
4797 /* }}} */
4798 
4799 /* {{{ proto bool array_multisort(array &$array1 [, mixed $array1_sort_order [, mixed $array1_sort_flags [, mixed ... ]]]
4800    Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
4801 PHP_FUNCTION(array_multisort)
4802 {
4803         zval*                   args;
4804         zval**                  arrays;
4805         Bucket**                indirect;
4806         uint            idx;
4807         Bucket*                 p;
4808         HashTable*              hash;
4809         int                             argc;
4810         int                             array_size;
4811         int                             num_arrays = 0;
4812         int                             parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
4813         int                             sort_order = PHP_SORT_ASC;
4814         int                             sort_type  = PHP_SORT_REGULAR;
4815         int                             i, k, n;
4816 
4817         if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
4818                 return;
4819         }
4820 
4821         /* Allocate space for storing pointers to input arrays and sort flags. */
4822         arrays = (zval **)ecalloc(argc, sizeof(zval *));
4823         for (i = 0; i < MULTISORT_LAST; i++) {
4824                 parse_state[i] = 0;
4825         }
4826         ARRAYG(multisort_func) = (compare_func_t*)ecalloc(argc, sizeof(compare_func_t));
4827 
4828         /* Here we go through the input arguments and parse them. Each one can
4829          * be either an array or a sort flag which follows an array. If not
4830          * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
4831          * accordingly. There can't be two sort flags of the same type after an
4832          * array, and the very first argument has to be an array. */
4833         for (i = 0; i < argc; i++) {
4834                 zval *arg = &args[i];
4835 
4836                 ZVAL_DEREF(arg);
4837                 if (Z_TYPE_P(arg) == IS_ARRAY) {
4838                         SEPARATE_ARRAY(arg);
4839                         /* We see the next array, so we update the sort flags of
4840                          * the previous array and reset the sort flags. */
4841                         if (i > 0) {
4842                                 ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
4843                                 sort_order = PHP_SORT_ASC;
4844                                 sort_type = PHP_SORT_REGULAR;
4845                         }
4846                         arrays[num_arrays++] = arg;
4847 
4848                         /* Next one may be an array or a list of sort flags. */
4849                         for (k = 0; k < MULTISORT_LAST; k++) {
4850                                 parse_state[k] = 1;
4851                         }
4852                 } else if (Z_TYPE_P(arg) == IS_LONG) {
4853                         switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
4854                                 case PHP_SORT_ASC:
4855                                 case PHP_SORT_DESC:
4856                                         /* flag allowed here */
4857                                         if (parse_state[MULTISORT_ORDER] == 1) {
4858                                                 /* Save the flag and make sure then next arg is not the current flag. */
4859                                                 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
4860                                                 parse_state[MULTISORT_ORDER] = 0;
4861                                         } else {
4862                                                 php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
4863                                                 MULTISORT_ABORT;
4864                                         }
4865                                         break;
4866 
4867                                 case PHP_SORT_REGULAR:
4868                                 case PHP_SORT_NUMERIC:
4869                                 case PHP_SORT_STRING:
4870                                 case PHP_SORT_NATURAL:
4871 #if HAVE_STRCOLL
4872                                 case PHP_SORT_LOCALE_STRING:
4873 #endif
4874                                         /* flag allowed here */
4875                                         if (parse_state[MULTISORT_TYPE] == 1) {
4876                                                 /* Save the flag and make sure then next arg is not the current flag. */
4877                                                 sort_type = (int)Z_LVAL_P(arg);
4878                                                 parse_state[MULTISORT_TYPE] = 0;
4879                                         } else {
4880                                                 php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
4881                                                 MULTISORT_ABORT;
4882                                         }
4883                                         break;
4884 
4885                                 default:
4886                                         php_error_docref(NULL, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
4887                                         MULTISORT_ABORT;
4888                                         break;
4889 
4890                         }
4891                 } else {
4892                         php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
4893                         MULTISORT_ABORT;
4894                 }
4895         }
4896         /* Take care of the last array sort flags. */
4897         ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
4898 
4899         /* Make sure the arrays are of the same size. */
4900         array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
4901         for (i = 0; i < num_arrays; i++) {
4902                 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
4903                         php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent");
4904                         MULTISORT_ABORT;
4905                 }
4906         }
4907 
4908         /* If all arrays are empty we don't need to do anything. */
4909         if (array_size < 1) {
4910                 efree(ARRAYG(multisort_func));
4911                 efree(arrays);
4912                 RETURN_TRUE;
4913         }
4914 
4915         /* Create the indirection array. This array is of size MxN, where
4916          * M is the number of entries in each input array and N is the number
4917          * of the input arrays + 1. The last column is NULL to indicate the end
4918          * of the row. */
4919         indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
4920         for (i = 0; i < array_size; i++) {
4921                 indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
4922         }
4923         for (i = 0; i < num_arrays; i++) {
4924                 k = 0;
4925                 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
4926                         p = Z_ARRVAL_P(arrays[i])->arData + idx;
4927                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
4928                         indirect[k][i] = *p;
4929                         k++;
4930                 }
4931         }
4932         for (k = 0; k < array_size; k++) {
4933                 ZVAL_UNDEF(&indirect[k][num_arrays].val);
4934         }
4935 
4936         /* Do the actual sort magic - bada-bim, bada-boom. */
4937         zend_qsort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
4938 
4939         /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
4940         HANDLE_BLOCK_INTERRUPTIONS();
4941         for (i = 0; i < num_arrays; i++) {
4942                 int repack;
4943 
4944                 hash = Z_ARRVAL_P(arrays[i]);
4945                 hash->nNumUsed = array_size;
4946                 hash->nInternalPointer = 0;
4947                 repack = !(hash->u.flags & HASH_FLAG_PACKED);
4948 
4949                 for (n = 0, k = 0; k < array_size; k++) {
4950                         hash->arData[k] = indirect[k][i];
4951                         if (hash->arData[k].key == NULL) {
4952                                 hash->arData[k].h = n++;
4953                         } else {
4954                                 repack = 0;
4955                         }
4956                 }
4957                 hash->nNextFreeElement = array_size;
4958                 if (repack) {
4959                         zend_hash_to_packed(hash);
4960                 } else {
4961                         zend_hash_rehash(hash);
4962                 }
4963         }
4964         HANDLE_UNBLOCK_INTERRUPTIONS();
4965 
4966         /* Clean up. */
4967         for (i = 0; i < array_size; i++) {
4968                 efree(indirect[i]);
4969         }
4970         efree(indirect);
4971         efree(ARRAYG(multisort_func));
4972         efree(arrays);
4973         RETURN_TRUE;
4974 }
4975 /* }}} */
4976 
4977 /* {{{ proto mixed array_rand(array input [, int num_req])
4978    Return key/keys for random entry/entries in the array */
4979 PHP_FUNCTION(array_rand)
4980 {
4981         zval *input;
4982         zend_long randval, num_req = 1;
4983         int num_avail;
4984         zend_string *string_key;
4985         zend_ulong num_key;
4986 
4987         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) {
4988                 return;
4989         }
4990 
4991         num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
4992 
4993         if (ZEND_NUM_ARGS() > 1) {
4994                 if (num_req <= 0 || num_req > num_avail) {
4995                         php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
4996                         return;
4997                 }
4998         }
4999 
5000         /* Make the return value an array only if we need to pass back more than one result. */
5001         if (num_req > 1) {
5002                 array_init_size(return_value, (uint32_t)num_req);
5003         }
5004 
5005         /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
5006         ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
5007                 if (!num_req) {
5008                         break;
5009                 }
5010 
5011                 randval = php_rand();
5012 
5013                 if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
5014                         /* If we are returning a single result, just do it. */
5015                         if (Z_TYPE_P(return_value) != IS_ARRAY) {
5016                                 if (string_key) {
5017                                         RETURN_STR_COPY(string_key);
5018                                 } else {
5019                                         RETURN_LONG(num_key);
5020                                 }
5021                         } else {
5022                                 /* Append the result to the return value. */
5023                                 if (string_key) {
5024                                         add_next_index_str(return_value, zend_string_copy(string_key));
5025                                 } else {
5026                                         add_next_index_long(return_value, num_key);
5027                                 }
5028                         }
5029                         num_req--;
5030                 }
5031                 num_avail--;
5032         } ZEND_HASH_FOREACH_END();
5033 }
5034 /* }}} */
5035 
5036 /* {{{ proto mixed array_sum(array input)
5037    Returns the sum of the array entries */
5038 PHP_FUNCTION(array_sum)
5039 {
5040         zval *input,
5041                  *entry,
5042                  entry_n;
5043 
5044         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
5045                 return;
5046         }
5047 
5048         ZVAL_LONG(return_value, 0);
5049 
5050         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
5051                 if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
5052                         continue;
5053                 }
5054                 ZVAL_COPY(&entry_n, entry);
5055                 convert_scalar_to_number(&entry_n);
5056                 fast_add_function(return_value, return_value, &entry_n);
5057         } ZEND_HASH_FOREACH_END();
5058 }
5059 /* }}} */
5060 
5061 /* {{{ proto mixed array_product(array input)
5062    Returns the product of the array entries */
5063 PHP_FUNCTION(array_product)
5064 {
5065         zval *input,
5066                  *entry,
5067                  entry_n;
5068         double dval;
5069 
5070         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
5071                 return;
5072         }
5073 
5074         ZVAL_LONG(return_value, 1);
5075         if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
5076                 return;
5077         }
5078 
5079         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
5080                 if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
5081                         continue;
5082                 }
5083                 ZVAL_COPY(&entry_n, entry);
5084                 convert_scalar_to_number(&entry_n);
5085 
5086                 if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
5087                         dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
5088                         if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
5089                                 Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
5090                                 continue;
5091                         }
5092                 }
5093                 convert_to_double(return_value);
5094                 convert_to_double(&entry_n);
5095                 Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
5096         } ZEND_HASH_FOREACH_END();
5097 }
5098 /* }}} */
5099 
5100 /* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
5101    Iteratively reduce the array to a single value via the callback. */
5102 PHP_FUNCTION(array_reduce)
5103 {
5104         zval *input;
5105         zval args[2];
5106         zval *operand;
5107         zval result;
5108         zval retval;
5109         zend_fcall_info fci;
5110         zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5111         zval *initial = NULL;
5112         HashTable *htbl;
5113 
5114         if (zend_parse_parameters(ZEND_NUM_ARGS(), "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
5115                 return;
5116         }
5117 
5118 
5119         if (ZEND_NUM_ARGS() > 2) {
5120                 ZVAL_DUP(&result, initial);
5121         } else {
5122                 ZVAL_NULL(&result);
5123         }
5124 
5125         /* (zval **)input points to an element of argument stack
5126          * the base pointer of which is subject to change.
5127          * thus we need to keep the pointer to the hashtable for safety */
5128         htbl = Z_ARRVAL_P(input);
5129 
5130         if (zend_hash_num_elements(htbl) == 0) {
5131                 ZVAL_COPY_VALUE(return_value, &result);
5132                 return;
5133         }
5134 
5135         fci.retval = &retval;
5136         fci.param_count = 2;
5137         fci.no_separation = 0;
5138 
5139         ZEND_HASH_FOREACH_VAL(htbl, operand) {
5140                 ZVAL_COPY(&args[0], &result);
5141                 ZVAL_COPY(&args[1], operand);
5142                 fci.params = args;
5143 
5144                 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
5145                         zval_ptr_dtor(&args[1]);
5146                         zval_ptr_dtor(&args[0]);
5147                         zval_ptr_dtor(&result);
5148                         ZVAL_COPY_VALUE(&result, &retval);
5149                 } else {
5150                         zval_ptr_dtor(&args[1]);
5151                         zval_ptr_dtor(&args[0]);
5152                         return;
5153                 }
5154         } ZEND_HASH_FOREACH_END();
5155 
5156         RETVAL_ZVAL(&result, 1, 1);
5157 }
5158 /* }}} */
5159 
5160 /* {{{ proto array array_filter(array input [, mixed callback])
5161    Filters elements from the array via the callback. */
5162 PHP_FUNCTION(array_filter)
5163 {
5164         zval *array;
5165         zval *operand;
5166         zval *key;
5167         zval args[2];
5168         zval retval;
5169         zend_bool have_callback = 0;
5170         zend_long use_type = 0;
5171         zend_string *string_key;
5172         zend_fcall_info fci = empty_fcall_info;
5173         zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5174         zend_ulong num_key;
5175 
5176         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
5177                 return;
5178         }
5179 
5180         array_init(return_value);
5181         if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
5182                 return;
5183         }
5184 
5185         if (ZEND_NUM_ARGS() > 1) {
5186                 have_callback = 1;
5187                 fci.no_separation = 0;
5188                 fci.retval = &retval;
5189                 if (use_type == ARRAY_FILTER_USE_BOTH) {
5190                         fci.param_count = 2;
5191                         key = &args[1];
5192                 } else {
5193                         fci.param_count = 1;
5194                         key = &args[0];
5195                 }
5196         }
5197 
5198         ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, string_key, operand) {
5199                 if (have_callback) {
5200                         if (use_type) {
5201                                 /* Set up the key */
5202                                 if (!string_key) {
5203                                         ZVAL_LONG(key, num_key);
5204                                 } else {
5205                                         ZVAL_STR_COPY(key, string_key);
5206                                 }
5207                         }
5208                         if (use_type != ARRAY_FILTER_USE_KEY) {
5209                                 ZVAL_COPY(&args[0], operand);
5210                         }
5211                         fci.params = args;
5212 
5213                         if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
5214                                 zval_ptr_dtor(&args[0]);
5215                                 if (use_type == ARRAY_FILTER_USE_BOTH) {
5216                                         zval_ptr_dtor(&args[1]);
5217                                 }
5218                                 if (!Z_ISUNDEF(retval)) {
5219                                         int retval_true = zend_is_true(&retval);
5220 
5221                                         zval_ptr_dtor(&retval);
5222                                         if (!retval_true) {
5223                                                 continue;
5224                                         }
5225                                 } else {
5226                                         continue;
5227                                 }
5228                         } else {
5229                                 zval_ptr_dtor(&args[0]);
5230                                 if (use_type == ARRAY_FILTER_USE_BOTH) {
5231                                         zval_ptr_dtor(&args[1]);
5232                                 }
5233                                 return;
5234                         }
5235                 } else if (!zend_is_true(operand)) {
5236                         continue;
5237                 }
5238 
5239                 if (string_key) {
5240                         operand = zend_hash_update(Z_ARRVAL_P(return_value), string_key, operand);
5241                 } else {
5242                         operand = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand);
5243                 }
5244                 zval_add_ref(operand);
5245         } ZEND_HASH_FOREACH_END();
5246 }
5247 /* }}} */
5248 
5249 /* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
5250    Applies the callback to the elements in given arrays. */
5251 PHP_FUNCTION(array_map)
5252 {
5253         zval *arrays = NULL;
5254         int n_arrays = 0;
5255         zval result;
5256         zend_fcall_info fci = empty_fcall_info;
5257         zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
5258         int i;
5259         uint32_t k, maxlen = 0;
5260 
5261 #ifndef FAST_ZPP
5262         if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) {
5263                 return;
5264         }
5265 #else
5266         ZEND_PARSE_PARAMETERS_START(2, -1)
5267                 Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
5268                 Z_PARAM_VARIADIC('+', arrays, n_arrays)
5269         ZEND_PARSE_PARAMETERS_END();
5270 #endif
5271 
5272         RETVAL_NULL();
5273 
5274         if (n_arrays == 1) {
5275                 zend_ulong num_key;
5276                 zend_string *str_key;
5277                 zval *zv, arg;
5278 
5279                 if (Z_TYPE(arrays[0]) != IS_ARRAY) {
5280                         php_error_docref(NULL, E_WARNING, "Argument #%d should be an array", 2);
5281                         return;
5282                 }
5283                 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
5284 
5285                 /* Short-circuit: if no callback and only one array, just return it. */
5286                 if (!ZEND_FCI_INITIALIZED(fci)) {
5287                         ZVAL_COPY(return_value, &arrays[0]);
5288                         return;
5289                 }
5290 
5291                 array_init_size(return_value, maxlen);
5292 
5293                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
5294                         fci.retval = &result;
5295                         fci.param_count = 1;
5296                         fci.params = &arg;
5297                         fci.no_separation = 0;
5298 
5299                         ZVAL_COPY(&arg, zv);
5300 
5301                         if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
5302                                 zval_dtor(return_value);
5303                                 zval_ptr_dtor(&arg);
5304                                 RETURN_NULL();
5305                         } else {
5306                                 zval_ptr_dtor(&arg);
5307                         }
5308                         if (str_key) {
5309                                 zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result);
5310                         } else {
5311                                 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
5312                         }
5313                 } ZEND_HASH_FOREACH_END();
5314         } else {
5315                 uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
5316 
5317                 for (i = 0; i < n_arrays; i++) {
5318                         if (Z_TYPE(arrays[i]) != IS_ARRAY) {
5319                                 php_error_docref(NULL, E_WARNING, "Argument #%d should be an array", i + 2);
5320                                 efree(array_pos);
5321                                 return;
5322                         }
5323                         if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
5324                                 maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
5325                         }
5326                 }
5327 
5328                 array_init_size(return_value, maxlen);
5329 
5330                 if (!ZEND_FCI_INITIALIZED(fci)) {
5331                         zval zv;
5332 
5333                         /* We iterate through all the arrays at once. */
5334                         for (k = 0; k < maxlen; k++) {
5335 
5336                                 /* If no callback, the result will be an array, consisting of current
5337                                  * entries from all arrays. */
5338                                 array_init_size(&result, n_arrays);
5339 
5340                                 for (i = 0; i < n_arrays; i++) {
5341                                         /* If this array still has elements, add the current one to the
5342                                          * parameter list, otherwise use null value. */
5343                                         uint32_t pos = array_pos[i];
5344                                         while (1) {
5345                                                 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
5346                                                         ZVAL_NULL(&zv);
5347                                                         break;
5348                                                 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
5349                                                         ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
5350                                                         array_pos[i] = pos + 1;
5351                                                         break;
5352                                                 }
5353                                                 pos++;
5354                                         }
5355 
5356                                         zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
5357                                 }
5358 
5359                                 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
5360                         }
5361                 } else {
5362                         zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
5363 
5364                         /* We iterate through all the arrays at once. */
5365                         for (k = 0; k < maxlen; k++) {
5366                                 for (i = 0; i < n_arrays; i++) {
5367                                         /* If this array still has elements, add the current one to the
5368                                          * parameter list, otherwise use null value. */
5369                                         uint32_t pos = array_pos[i];
5370                                         while (1) {
5371                                                 if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
5372                                                         ZVAL_NULL(&params[i]);
5373                                                         break;
5374                                                 } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
5375                                                         ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
5376                                                         array_pos[i] = pos + 1;
5377                                                         break;
5378                                                 }
5379                                                 pos++;
5380                                         }
5381                                 }
5382 
5383                                 fci.retval = &result;
5384                                 fci.param_count = n_arrays;
5385                                 fci.params = params;
5386                                 fci.no_separation = 0;
5387 
5388                                 if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
5389                                         efree(array_pos);
5390                                         zval_dtor(return_value);
5391                                         for (i = 0; i < n_arrays; i++) {
5392                                                 zval_ptr_dtor(&params[i]);
5393                                         }
5394                                         efree(params);
5395                                         RETURN_NULL();
5396                                 } else {
5397                                         for (i = 0; i < n_arrays; i++) {
5398                                                 zval_ptr_dtor(&params[i]);
5399                                         }
5400                                 }
5401 
5402                                 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
5403                         }
5404 
5405                         efree(params);
5406                 }
5407                 efree(array_pos);
5408         }
5409 }
5410 /* }}} */
5411 
5412 /* {{{ proto bool array_key_exists(mixed key, array search)
5413    Checks if the given key or index exists in the array */
5414 PHP_FUNCTION(array_key_exists)
5415 {
5416         zval *key;                                      /* key to check for */
5417         HashTable *array;                       /* array to check in */
5418 
5419 #ifndef FAST_ZPP
5420         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zH", &key, &array) == FAILURE) {
5421                 return;
5422         }
5423 #else
5424         ZEND_PARSE_PARAMETERS_START(2, 2)
5425                 Z_PARAM_ZVAL(key)
5426                 Z_PARAM_ARRAY_OR_OBJECT_HT(array)
5427         ZEND_PARSE_PARAMETERS_END();
5428 #endif
5429 
5430         switch (Z_TYPE_P(key)) {
5431                 case IS_STRING:
5432                         if (zend_symtable_exists_ind(array, Z_STR_P(key))) {
5433                                 RETURN_TRUE;
5434                         }
5435                         RETURN_FALSE;
5436                 case IS_LONG:
5437                         if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
5438                                 RETURN_TRUE;
5439                         }
5440                         RETURN_FALSE;
5441                 case IS_NULL:
5442                         if (zend_hash_exists_ind(array, ZSTR_EMPTY_ALLOC())) {
5443                                 RETURN_TRUE;
5444                         }
5445                         RETURN_FALSE;
5446 
5447                 default:
5448                         php_error_docref(NULL, E_WARNING, "The first argument should be either a string or an integer");
5449                         RETURN_FALSE;
5450         }
5451 }
5452 /* }}} */
5453 
5454 /* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
5455    Split array into chunks */
5456 PHP_FUNCTION(array_chunk)
5457 {
5458         int argc = ZEND_NUM_ARGS(), num_in;
5459         zend_long size, current = 0;
5460         zend_string *str_key;
5461         zend_ulong num_key;
5462         zend_bool preserve_keys = 0;
5463         zval *input = NULL;
5464         zval chunk;
5465         zval *entry;
5466 
5467         if (zend_parse_parameters(argc, "al|b", &input, &size, &preserve_keys) == FAILURE) {
5468                 return;
5469         }
5470         /* Do bounds checking for size parameter. */
5471         if (size < 1) {
5472                 php_error_docref(NULL, E_WARNING, "Size parameter expected to be greater than 0");
5473                 return;
5474         }
5475 
5476         num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
5477 
5478         if (size > num_in) {
5479                 size = num_in > 0 ? num_in : 1;
5480         }
5481 
5482         array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
5483 
5484         ZVAL_UNDEF(&chunk);
5485 
5486         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
5487                 /* If new chunk, create and initialize it. */
5488                 if (Z_TYPE(chunk) == IS_UNDEF) {
5489                         array_init_size(&chunk, (uint32_t)size);
5490                 }
5491 
5492                 /* Add entry to the chunk, preserving keys if necessary. */
5493                 if (preserve_keys) {
5494                         if (str_key) {
5495                                 entry = zend_hash_update(Z_ARRVAL(chunk), str_key, entry);
5496                         } else {
5497                                 entry = zend_hash_index_update(Z_ARRVAL(chunk), num_key, entry);
5498                         }
5499                 } else {
5500                         entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
5501                 }
5502                 zval_add_ref(entry);
5503 
5504                 /* If reached the chunk size, add it to the result array, and reset the
5505                  * pointer. */
5506                 if (!(++current % size)) {
5507                         add_next_index_zval(return_value, &chunk);
5508                         ZVAL_UNDEF(&chunk);
5509                 }
5510         } ZEND_HASH_FOREACH_END();
5511 
5512         /* Add the final chunk if there is one. */
5513         if (Z_TYPE(chunk) != IS_UNDEF) {
5514                 add_next_index_zval(return_value, &chunk);
5515         }
5516 }
5517 /* }}} */
5518 
5519 /* {{{ proto array array_combine(array keys, array values)
5520    Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
5521 PHP_FUNCTION(array_combine)
5522 {
5523         HashTable *values, *keys;
5524         uint32_t pos_values = 0;
5525         zval *entry_keys, *entry_values;
5526         int num_keys, num_values;
5527 
5528         if (zend_parse_parameters(ZEND_NUM_ARGS(), "hh", &keys, &values) == FAILURE) {
5529                 return;
5530         }
5531 
5532         num_keys = zend_hash_num_elements(keys);
5533         num_values = zend_hash_num_elements(values);
5534 
5535         if (num_keys != num_values) {
5536                 php_error_docref(NULL, E_WARNING, "Both parameters should have an equal number of elements");
5537                 RETURN_FALSE;
5538         }
5539 
5540         array_init_size(return_value, num_keys);
5541 
5542         if (!num_keys) {
5543                 return;
5544         }
5545 
5546         ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
5547                 while (1) {
5548                         if (pos_values >= values->nNumUsed) {
5549                                 break;
5550                         } else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) {
5551                                 entry_values = &values->arData[pos_values].val;
5552                                 if (Z_TYPE_P(entry_keys) == IS_LONG) {
5553                                         entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
5554                                                 Z_LVAL_P(entry_keys), entry_values);
5555                                 } else {
5556                                         zend_string *key = zval_get_string(entry_keys);
5557                                         entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
5558                                                 key, entry_values);
5559                                         zend_string_release(key);
5560                                 }
5561                                 zval_add_ref(entry_values);
5562                                 pos_values++;
5563                                 break;
5564                         }
5565                         pos_values++;
5566                 }
5567         } ZEND_HASH_FOREACH_END();
5568 }
5569 /* }}} */
5570 
5571 /*
5572  * Local variables:
5573  * tab-width: 4
5574  * c-basic-offset: 4
5575  * End:
5576  * vim600: noet sw=4 ts=4 fdm=marker
5577  * vim<600: noet sw=4 ts=4
5578  */

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