LTP GCOV extension - code coverage report
Current view: directory - ext/standard - array.c
Test: PHP Code Coverage
Date: 2007-04-10 Instrumented lines: 2142
Code covered: 12.7 % Executed lines: 273
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5