diff options
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r-- | ext/standard/array.c | 1256 |
1 files changed, 849 insertions, 407 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c index 696ad05ff6..cba3b5d46e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 7 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2015 The PHP Group | + | Copyright (c) 1997-2016 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -46,9 +46,7 @@ #include "php_string.h" #include "php_rand.h" #include "zend_smart_str.h" -#ifdef HAVE_SPL #include "ext/spl/spl_array.h" -#endif /* {{{ defines */ #define EXTR_OVERWRITE 0 @@ -81,8 +79,6 @@ #define INTERSECT_COMP_DATA_USER 1 #define INTERSECT_COMP_KEY_INTERNAL 0 #define INTERSECT_COMP_KEY_USER 1 - -#define DOUBLE_DRIFT_FIX 0.000000000000001 /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(array) @@ -141,75 +137,561 @@ PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */ } /* }}} */ -static void php_set_compare_func(zend_long sort_type) /* {{{ */ +static int php_array_key_compare(const void *a, const void *b) /* {{{ */ { - switch (sort_type & ~PHP_SORT_FLAG_CASE) { - case PHP_SORT_NUMERIC: - ARRAYG(compare_func) = numeric_compare_function; - break; + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + zend_uchar t; + zend_long l1, l2; + double d; - case PHP_SORT_STRING: - ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_case_compare_function : string_compare_function; - break; + if (f->key == NULL) { + if (s->key == NULL) { + return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + } else { + l1 = (zend_long)f->h; + t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL((double)l1 - d); + } else { + l2 = 0; + } + } + } else { + if (s->key) { + return zendi_smart_strcmp(f->key, s->key); + } else { + l2 = (zend_long)s->h; + t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL(d - (double)l2); + } else { + l1 = 0; + } + } + } + return l1 > l2 ? 1 : (l1 < l2 ? -1 : 0); +} +/* }}} */ - case PHP_SORT_NATURAL: - ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_natural_case_compare_function : string_natural_compare_function; - break; +static int php_array_reverse_key_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare(b, a); +} +/* }}} */ + +static int php_array_key_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + + if (f->key == NULL && s->key == NULL) { + return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + } else { + double d1, d2; + if (f->key) { + d1 = zend_strtod(f->key->val, NULL); + } else { + d1 = (double)(zend_long)f->h; + } + if (s->key) { + d2 = zend_strtod(s->key->val, NULL); + } else { + d2 = (double)(zend_long)s->h; + } + return ZEND_NORMALIZE_BOOL(d1 - d2); + } +} +/* }}} */ + +static int php_array_reverse_key_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_numeric(b, a); +} +/* }}} */ + +static int php_array_key_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; + } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s1; + } + return zend_binary_strcasecmp_l(s1, l1, s2, l2); +} +/* }}} */ + +static int php_array_reverse_key_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_case(b, a); +} +/* }}} */ + +static int php_array_key_compare_string(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; + } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s2; + } + return zend_binary_strcmp(s1, l1, s2, l2); +} +/* }}} */ + +static int php_array_reverse_key_compare_string(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string(b, a); +} +/* }}} */ + +static int php_array_key_compare_string_natural_general(const void *a, const void *b, int fold_case) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; + } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s1; + } + return strnatcmp_ex(s1, l1, s2, l2, fold_case); +} +/* }}} */ + +static int php_array_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(a, b, 1); +} +/* }}} */ + +static int php_array_reverse_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(b, a, 1); +} +/* }}} */ + +static int php_array_key_compare_string_natural(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(a, b, 0); +} +/* }}} */ + +static int php_array_reverse_key_compare_string_natural(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(b, a, 0); +} +/* }}} */ #if HAVE_STRCOLL - case PHP_SORT_LOCALE_STRING: - ARRAYG(compare_func) = string_locale_compare_function; - break; -#endif +static int php_array_key_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; - case PHP_SORT_REGULAR: - default: - ARRAYG(compare_func) = zval_compare_function; - break; + if (f->key) { + s1 = f->key->val; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); } + if (s->key) { + s2 = s->key->val; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + } + return strcoll(s1, s2); } /* }}} */ -static int php_array_key_compare(const void *a, const void *b) /* {{{ */ +static int php_array_reverse_key_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_locale(b, a); +} +/* }}} */ +#endif + +/* Numbers are always smaller than strings int this function as it + * anyway doesn't make much sense to compare two different data types. + * This keeps it consistent and simple. + * + * This is not correct any more, depends on what compare_func is set to. + */ +static int php_array_data_compare(const void *a, const void *b) /* {{{ */ { Bucket *f; Bucket *s; zval result; - zval first; - zval second; + zval *first; + zval *second; f = (Bucket *) a; s = (Bucket *) b; - if (f->key == NULL) { - ZVAL_LONG(&first, f->h); - } else { - ZVAL_STR(&first, f->key); + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + if (compare_function(&result, first, second) == FAILURE) { + return 0; } - if (s->key == NULL) { - ZVAL_LONG(&second, s->h); - } else { - ZVAL_STR(&second, s->key); + ZEND_ASSERT(Z_TYPE(result) == IS_LONG); + return Z_LVAL(result); +} +/* }}} */ + +static int php_array_reverse_data_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare(a, b) * -1; +} +/* }}} */ + +static int php_array_data_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); } - if (ARRAYG(compare_func)(&result, &first, &second) == FAILURE) { - return 0; + return numeric_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_numeric(b, a); +} +/* }}} */ + +static int php_array_data_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + + return string_case_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string_case(b, a); +} +/* }}} */ + +static int php_array_data_compare_string(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; - if (EXPECTED(Z_TYPE(result) == IS_LONG)) { - return ZEND_NORMALIZE_BOOL(Z_LVAL(result)); - } else if (Z_TYPE(result) == IS_DOUBLE) { - return ZEND_NORMALIZE_BOOL(Z_DVAL(result)); + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); } - return ZEND_NORMALIZE_BOOL(zval_get_long(&result)); + return string_compare_function(first, second); } /* }}} */ -static int php_array_reverse_key_compare(const void *a, const void *b) /* {{{ */ +static int php_array_reverse_data_compare_string(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string(b, a); +} +/* }}} */ + +static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + zend_string *str1 = zval_get_string(&f->val); + zend_string *str2 = zval_get_string(&s->val); + + int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case); + + zend_string_release(str1); + zend_string_release(str2); + return result; +} +/* }}} */ + +static int php_array_natural_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(a, b, 0); +} +/* }}} */ + +static int php_array_reverse_natural_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(b, a, 0); +} +/* }}} */ + +static int php_array_natural_case_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(a, b, 1); +} +/* }}} */ + +static int php_array_reverse_natural_case_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(b, a, 1); +} +/* }}} */ + +#if HAVE_STRCOLL +static int php_array_data_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + + return string_locale_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string_locale(b, a); +} +/* }}} */ +#endif + +static compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */ +{ + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + if (reverse) { + return php_array_reverse_key_compare_numeric; + } else { + return php_array_key_compare_numeric; + } + break; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_key_compare_string_case; + } else { + return php_array_key_compare_string_case; + } + } else { + if (reverse) { + return php_array_reverse_key_compare_string; + } else { + return php_array_key_compare_string; + } + } + break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_key_compare_string_natural_case; + } else { + return php_array_key_compare_string_natural_case; + } + } else { + if (reverse) { + return php_array_reverse_key_compare_string_natural; + } else { + return php_array_key_compare_string_natural; + } + } + break; + +#if HAVE_STRCOLL + case PHP_SORT_LOCALE_STRING: + if (reverse) { + return php_array_reverse_key_compare_string_locale; + } else { + return php_array_key_compare_string_locale; + } + break; +#endif + + case PHP_SORT_REGULAR: + default: + if (reverse) { + return php_array_reverse_key_compare; + } else { + return php_array_key_compare; + } + break; + } + return NULL; +} +/* }}} */ + +static compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */ { - return php_array_key_compare(a, b) * -1; + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + if (reverse) { + return php_array_reverse_data_compare_numeric; + } else { + return php_array_data_compare_numeric; + } + break; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_data_compare_string_case; + } else { + return php_array_data_compare_string_case; + } + } else { + if (reverse) { + return php_array_reverse_data_compare_string; + } else { + return php_array_data_compare_string; + } + } + break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_natural_case_compare; + } else { + return php_array_natural_case_compare; + } + } else { + if (reverse) { + return php_array_reverse_natural_compare; + } else { + return php_array_natural_compare; + } + } + break; + +#if HAVE_STRCOLL + case PHP_SORT_LOCALE_STRING: + if (reverse) { + return php_array_reverse_data_compare_string_locale; + } else { + return php_array_data_compare_string_locale; + } + break; +#endif + + case PHP_SORT_REGULAR: + default: + if (reverse) { + return php_array_reverse_data_compare; + } else { + return php_array_data_compare; + } + break; + } + return NULL; } /* }}} */ @@ -219,6 +701,7 @@ PHP_FUNCTION(krsort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; #ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { @@ -232,9 +715,9 @@ PHP_FUNCTION(krsort) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #endif - php_set_compare_func(sort_type); + cmp = php_get_key_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_reverse_key_compare, 0) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -247,6 +730,7 @@ PHP_FUNCTION(ksort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; #ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { @@ -260,9 +744,9 @@ PHP_FUNCTION(ksort) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #endif - php_set_compare_func(sort_type); + cmp = php_get_key_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_key_compare, 0) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -280,7 +764,7 @@ PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */ return 0; } - cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); + cnt = zend_array_count(Z_ARRVAL_P(array)); if (mode == COUNT_RECURSIVE) { if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { Z_ARRVAL_P(array)->u.v.nApplyCount++; @@ -325,7 +809,7 @@ PHP_FUNCTION(count) RETURN_LONG(0); break; case IS_ARRAY: - cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); + cnt = zend_array_count(Z_ARRVAL_P(array)); if (mode == COUNT_RECURSIVE) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { ZVAL_DEREF(element); @@ -335,9 +819,7 @@ PHP_FUNCTION(count) RETURN_LONG(cnt); break; case IS_OBJECT: { -#ifdef HAVE_SPL zval retval; -#endif /* first, we check if the handler is defined */ if (Z_OBJ_HT_P(array)->count_elements) { RETVAL_LONG(1); @@ -345,7 +827,6 @@ PHP_FUNCTION(count) return; } } -#ifdef HAVE_SPL /* if not and the object implements Countable we call its count() method */ if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) { zend_call_method_with_0_params(array, NULL, NULL, "count", &retval); @@ -355,7 +836,6 @@ PHP_FUNCTION(count) } return; } -#endif } default: RETURN_LONG(1); @@ -364,79 +844,6 @@ PHP_FUNCTION(count) } /* }}} */ -/* Numbers are always smaller than strings int this function as it - * anyway doesn't make much sense to compare two different data types. - * This keeps it consistent and simple. - * - * This is not correct any more, depends on what compare_func is set to. - */ -static int php_array_data_compare(const void *a, const void *b) /* {{{ */ -{ - Bucket *f; - Bucket *s; - zval result; - zval *first; - zval *second; - - f = (Bucket *) a; - s = (Bucket *) b; - - first = &f->val; - second = &s->val; - - if (Z_TYPE_P(first) == IS_INDIRECT) { - first = Z_INDIRECT_P(first); - } - if (Z_TYPE_P(second) == IS_INDIRECT) { - second = Z_INDIRECT_P(second); - } - if (ARRAYG(compare_func)(&result, first, second) == FAILURE) { - return 0; - } - - if (EXPECTED(Z_TYPE(result) == IS_LONG)) { - return ZEND_NORMALIZE_BOOL(Z_LVAL(result)); - } else if (Z_TYPE(result) == IS_DOUBLE) { - return ZEND_NORMALIZE_BOOL(Z_DVAL(result)); - } - - return ZEND_NORMALIZE_BOOL(zval_get_long(&result)); -} -/* }}} */ - -static int php_array_reverse_data_compare(const void *a, const void *b) /* {{{ */ -{ - return php_array_data_compare(a, b) * -1; -} -/* }}} */ - -static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */ -{ - Bucket *f = (Bucket *) a; - Bucket *s = (Bucket *) b; - zend_string *str1 = zval_get_string(&f->val); - zend_string *str2 = zval_get_string(&s->val); - - int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case); - - zend_string_release(str1); - zend_string_release(str2); - return result; -} -/* }}} */ - -static int php_array_natural_compare(const void *a, const void *b) /* {{{ */ -{ - return php_array_natural_general_compare(a, b, 0); -} -/* }}} */ - -static int php_array_natural_case_compare(const void *a, const void *b) /* {{{ */ -{ - return php_array_natural_general_compare(a, b, 1); -} -/* }}} */ - static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */ { zval *array; @@ -481,14 +888,15 @@ PHP_FUNCTION(asort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type); + cmp = php_get_data_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_data_compare, 0) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -501,14 +909,15 @@ PHP_FUNCTION(arsort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type); + cmp = php_get_data_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_reverse_data_compare, 0) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -521,14 +930,15 @@ PHP_FUNCTION(sort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type); + cmp = php_get_data_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_data_compare, 1) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -541,14 +951,15 @@ PHP_FUNCTION(rsort) { zval *array; zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type); + cmp = php_get_data_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_reverse_data_compare, 1) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -615,48 +1026,44 @@ static int php_array_user_compare(const void *a, const void *b) /* {{{ */ BG(user_compare_fci) = old_user_compare_fci; \ BG(user_compare_fci_cache) = old_user_compare_fci_cache; \ -/* {{{ proto bool usort(array array_arg, string cmp_function) - Sort an array by values using a user-defined comparison function */ -PHP_FUNCTION(usort) +static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */ { zval *array; - zend_refcounted *arr; - unsigned int refcount; + zend_array *arr; + zend_bool retval; PHP_ARRAY_CMP_FUNC_VARS; PHP_ARRAY_CMP_FUNC_BACKUP(); - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { PHP_ARRAY_CMP_FUNC_RESTORE(); return; } - /* Increase reference counter, so the attempts to modify the array in user - * comparison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. - */ - Z_ADDREF_P(array); - refcount = Z_REFCOUNT_P(array); - arr = Z_COUNTED_P(array); - - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_user_compare, 1) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function"); - if (--GC_REFCOUNT(arr) <= 0) { - _zval_dtor_func(arr ZEND_FILE_LINE_CC); - } - RETVAL_FALSE; - } else { - Z_DELREF_P(array); - RETVAL_TRUE; - } + arr = Z_ARR_P(array); + if (zend_hash_num_elements(arr) == 0) { + PHP_ARRAY_CMP_FUNC_RESTORE(); + RETURN_TRUE; } + /* Copy array, so the in-place modifications will not be visible to the callback function */ + arr = zend_array_dup(arr); + + retval = zend_hash_sort(arr, compare_func, renumber) != FAILURE; + + zval_ptr_dtor(array); + ZVAL_ARR(array, arr); + PHP_ARRAY_CMP_FUNC_RESTORE(); + RETURN_BOOL(retval); +} +/* }}} */ + +/* {{{ proto bool usort(array array_arg, string cmp_function) + Sort an array by values using a user-defined comparison function */ +PHP_FUNCTION(usort) +{ + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1); } /* }}} */ @@ -664,44 +1071,7 @@ PHP_FUNCTION(usort) Sort an array with a user-defined comparison function and maintain index association */ PHP_FUNCTION(uasort) { - zval *array; - zend_refcounted *arr; - unsigned int refcount; - PHP_ARRAY_CMP_FUNC_VARS; - - PHP_ARRAY_CMP_FUNC_BACKUP(); - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { - PHP_ARRAY_CMP_FUNC_RESTORE(); - return; - } - - /* Increase reference counter, so the attempts to modify the array in user - * comparison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. - */ - Z_ADDREF_P(array); - refcount = Z_REFCOUNT_P(array); - arr = Z_COUNTED_P(array); - - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_user_compare, 0) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function"); - if (--GC_REFCOUNT(arr) <= 0) { - _zval_dtor_func(arr ZEND_FILE_LINE_CC); - } - RETVAL_FALSE; - } else { - Z_DELREF_P(array); - RETVAL_TRUE; - } - } - - PHP_ARRAY_CMP_FUNC_RESTORE(); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0); } /* }}} */ @@ -752,44 +1122,7 @@ static int php_array_user_key_compare(const void *a, const void *b) /* {{{ */ Sort an array by keys using a user-defined comparison function */ PHP_FUNCTION(uksort) { - zval *array; - zend_refcounted *arr; - unsigned int refcount; - PHP_ARRAY_CMP_FUNC_VARS; - - PHP_ARRAY_CMP_FUNC_BACKUP(); - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { - PHP_ARRAY_CMP_FUNC_RESTORE(); - return; - } - - /* Increase reference counter, so the attempts to modify the array in user - * comparison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. - */ - Z_ADDREF_P(array); - refcount = Z_REFCOUNT_P(array); - arr = Z_COUNTED_P(array); - - if (zend_hash_sort(Z_ARRVAL_P(array), php_array_user_key_compare, 0) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function"); - if (--GC_REFCOUNT(arr) <= 0) { - _zval_dtor_func(arr ZEND_FILE_LINE_CC); - } - RETVAL_FALSE; - } else { - Z_DELREF_P(array); - RETVAL_TRUE; - } - } - - PHP_ARRAY_CMP_FUNC_RESTORE(); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0); } /* }}} */ @@ -990,8 +1323,6 @@ PHP_FUNCTION(min) return; } - php_set_compare_func(PHP_SORT_REGULAR); - /* mixed min ( array $values ) */ if (argc == 1) { zval *result; @@ -1039,8 +1370,6 @@ PHP_FUNCTION(max) return; } - php_set_compare_func(PHP_SORT_REGULAR); - /* mixed max ( array $values ) */ if (argc == 1) { zval *result; @@ -1084,7 +1413,6 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive) *zv; /* Set up known arguments */ - ZVAL_UNDEF(&retval); ZVAL_UNDEF(&args[1]); if (userdata) { ZVAL_COPY(&args[2], userdata); @@ -1112,12 +1440,9 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive) zend_fcall_info orig_array_walk_fci; zend_fcall_info_cache orig_array_walk_fci_cache; - if (Z_ISREF_P(zv)) { - thash = Z_ARRVAL_P(Z_REFVAL_P(zv)); - } else { - SEPARATE_ZVAL(zv); - thash = Z_ARRVAL_P(zv); - } + ZVAL_DEREF(zv); + SEPARATE_ARRAY(zv); + thash = Z_ARRVAL_P(zv); if (thash->u.v.nApplyCount > 1) { php_error_docref(NULL, E_WARNING, "recursion detected"); if (userdata) { @@ -1433,7 +1758,7 @@ PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t Imports variables into symbol table from an array */ PHP_FUNCTION(extract) { - zval *var_array, *prefix = NULL; + zval *var_array_param, *prefix = NULL; zend_long extract_type = EXTR_OVERWRITE; zval *entry; zend_string *var_name; @@ -1441,14 +1766,15 @@ PHP_FUNCTION(extract) int var_exists, count = 0; int extract_refs = 0; zend_array *symbol_table; + zval var_array; #ifndef FAST_ZPP - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|lz/", &var_array_param, &extract_type, &prefix) == FAILURE) { return; } #else ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ARRAY(var_array) + Z_PARAM_ARRAY(var_array_param) Z_PARAM_OPTIONAL Z_PARAM_LONG(extract_type) Z_PARAM_ZVAL_EX(prefix, 0, 1) @@ -1457,7 +1783,7 @@ PHP_FUNCTION(extract) extract_refs = (extract_type & EXTR_REFS); if (extract_refs) { - SEPARATE_ZVAL(var_array); + SEPARATE_ZVAL(var_array_param); } extract_type &= 0xff; @@ -1479,6 +1805,10 @@ PHP_FUNCTION(extract) } } + if (zend_forbid_dynamic_call("extract()") == FAILURE) { + return; + } + symbol_table = zend_rebuild_symbol_table(); #if 0 if (!symbol_table) { @@ -1487,7 +1817,11 @@ PHP_FUNCTION(extract) } #endif - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(var_array), num_key, var_name, entry) { + /* The array might be stored in a local variable that will be overwritten. To avoid losing the + * reference in that case we work on a copy. */ + ZVAL_COPY(&var_array, var_array_param); + + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(var_array), num_key, var_name, entry) { zval final_name; ZVAL_NULL(&final_name); @@ -1513,8 +1847,11 @@ PHP_FUNCTION(extract) if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { break; } - if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this") && EG(scope) && ZSTR_LEN(EG(scope)->name) != 0) { - break; + if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) { + zend_class_entry *scope = zend_get_executed_scope(); + if (scope && ZSTR_LEN(scope->name) != 0) { + break; + } } ZVAL_STR_COPY(&final_name, var_name); break; @@ -1555,8 +1892,8 @@ PHP_FUNCTION(extract) } if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zval *orig_var; if (extract_refs) { - zval *orig_var; ZVAL_MAKE_REF(entry); Z_ADDREF_P(entry); @@ -1571,13 +1908,24 @@ PHP_FUNCTION(extract) zend_hash_update(symbol_table, Z_STR(final_name), entry); } } else { + ZVAL_DEREF(entry); if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); - zend_hash_update_ind(symbol_table, Z_STR(final_name), entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_update(symbol_table, Z_STR(final_name), entry); + } } count++; } zval_dtor(&final_name); } ZEND_HASH_FOREACH_END(); + zval_ptr_dtor(&var_array); RETURN_LONG(count); } @@ -1590,7 +1938,8 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu ZVAL_DEREF(entry); if (Z_TYPE_P(entry) == IS_STRING) { if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) { - ZVAL_DUP(&data, value_ptr); + ZVAL_DEREF(value_ptr); + ZVAL_COPY(&data, value_ptr); zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data); } } else if (Z_TYPE_P(entry) == IS_ARRAY) { @@ -1624,7 +1973,14 @@ PHP_FUNCTION(compact) return; } + if (zend_forbid_dynamic_call("compact()") == FAILURE) { + return; + } + symbol_table = zend_rebuild_symbol_table(); + if (UNEXPECTED(symbol_table == NULL)) { + return; + } /* compact() is probably most used with a single array of var_names or multiple string names, rather than a combination of both. @@ -1648,34 +2004,73 @@ PHP_FUNCTION(array_fill) zval *val; zend_long start_key, num; +#ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &start_key, &num, &val) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(start_key) + Z_PARAM_LONG(num) + Z_PARAM_ZVAL(val) + ZEND_PARSE_PARAMETERS_END(); +#endif - if (num < 0) { - php_error_docref(NULL, E_WARNING, "Number of elements can't be negative"); - RETURN_FALSE; - } + if (EXPECTED(num > 0)) { + if (sizeof(num) > 4 && UNEXPECTED(EXPECTED(num > 0x7fffffff))) { + php_error_docref(NULL, E_WARNING, "Too many elements"); + RETURN_FALSE; + } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) { + php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied"); + RETURN_FALSE; + } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) { + /* create packed array */ + Bucket *p; + zend_long n; - /* allocate an array for return */ - array_init_size(return_value, (uint32_t)num); + array_init_size(return_value, (uint32_t)(start_key + num)); + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + Z_ARRVAL_P(return_value)->nNumUsed = start_key + num; + Z_ARRVAL_P(return_value)->nNumOfElements = num; + Z_ARRVAL_P(return_value)->nInternalPointer = start_key; + Z_ARRVAL_P(return_value)->nNextFreeElement = start_key + num; - if (num == 0) { - return; - } + if (Z_REFCOUNTED_P(val)) { + GC_REFCOUNT(Z_COUNTED_P(val)) += num; + } - num--; - zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, val); - Z_TRY_ADDREF_P(val); + p = Z_ARRVAL_P(return_value)->arData; + n = start_key; - while (num--) { - if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), val) != NULL) { - Z_TRY_ADDREF_P(val); + while (start_key--) { + ZVAL_UNDEF(&p->val); + p++; + } + while (num--) { + ZVAL_COPY_VALUE(&p->val, val); + p->h = n++; + p->key = NULL; + p++; + } } else { - zval_dtor(return_value); - php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied"); - RETURN_FALSE; + /* create hash */ + array_init_size(return_value, (uint32_t)num); + zend_hash_real_init(Z_ARRVAL_P(return_value), 0); + if (Z_REFCOUNTED_P(val)) { + GC_REFCOUNT(Z_COUNTED_P(val)) += num; + } + zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val); + while (--num) { + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val); + start_key++; + } } + } else if (EXPECTED(num == 0)) { + array_init(return_value); + return; + } else { + php_error_docref(NULL, E_WARNING, "Number of elements can't be negative"); + RETURN_FALSE; } } /* }}} */ @@ -1707,6 +2102,28 @@ PHP_FUNCTION(array_fill_keys) } /* }}} */ +#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \ + double __calc_size = ((start - end) / step) + 1; \ + if (__calc_size >= (double)HT_MAX_SIZE) { \ + php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \ + RETURN_FALSE; \ + } \ + size = (uint32_t)round(__calc_size); \ + array_init_size(return_value, size); \ + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \ + } while (0) + +#define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \ + zend_ulong __calc_size = (start - end) / lstep; \ + if (__calc_size >= HT_MAX_SIZE - 1) { \ + php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%pd end=%pd", end, start); \ + RETURN_FALSE; \ + } \ + size = (uint32_t)(__calc_size + 1); \ + array_init_size(return_value, size); \ + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \ + } while (0) + /* {{{ proto array range(mixed low, mixed high[, int step]) Create an array containing the range of integers or characters from low to high (inclusive) */ PHP_FUNCTION(range) @@ -1803,12 +2220,16 @@ PHP_FUNCTION(range) zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) { - double low, high, value; - zend_long i; + double low, high, element; + uint32_t i, size; double_str: low = zval_get_double(zlow); high = zval_get_double(zhigh); - i = 0; + + if (zend_isinf(high) || zend_isinf(low)) { + php_error_docref(NULL, E_WARNING, "Invalid range supplied: start=%0.0f end=%0.0f", low, high); + RETURN_FALSE; + } Z_TYPE_INFO(tmp) = IS_DOUBLE; if (low > high) { /* Negative steps */ @@ -1817,13 +2238,13 @@ double_str: goto err; } - array_init_size(return_value, (uint32_t)(((low - high) / step) + 1)); - zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) { - Z_DVAL(tmp) = value; - ZEND_HASH_FILL_ADD(&tmp); - } + for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step)) { + Z_DVAL(tmp) = element; + ZEND_HASH_FILL_ADD(&tmp); + } } ZEND_HASH_FILL_END(); } else if (high > low) { /* Positive steps */ if (high - low < step || step <= 0) { @@ -1831,11 +2252,11 @@ double_str: goto err; } - array_init_size(return_value, (uint32_t)(((high - low) / step) + 1)); - zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) { - Z_DVAL(tmp) = value; + for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step)) { + Z_DVAL(tmp) = element; ZEND_HASH_FILL_ADD(&tmp); } } ZEND_HASH_FILL_END(); @@ -1845,43 +2266,53 @@ double_str: zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } } else { - double low, high; - zend_long lstep; + zend_long low, high; + /* lstep is a ulong so that comparisons to it don't overflow, i.e. low - high < lstep */ + zend_ulong lstep; + uint32_t i, size; long_str: - low = zval_get_double(zlow); - high = zval_get_double(zhigh); - lstep = (zend_long) step; + low = zval_get_long(zlow); + high = zval_get_long(zhigh); + + if (step <= 0) { + err = 1; + goto err; + } + + lstep = step; Z_TYPE_INFO(tmp) = IS_LONG; if (low > high) { /* Negative steps */ - if (low - high < lstep || lstep <= 0) { + if (low - high < lstep) { err = 1; goto err; } - array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1)); - zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + + RANGE_CHECK_LONG_INIT_ARRAY(low, high); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (; low >= high; low -= lstep) { - Z_LVAL(tmp) = (zend_long)low; + for (i = 0; i < size; ++i) { + Z_LVAL(tmp) = low - (i * lstep); ZEND_HASH_FILL_ADD(&tmp); } } ZEND_HASH_FILL_END(); } else if (high > low) { /* Positive steps */ - if (high - low < lstep || lstep <= 0) { + if (high - low < lstep) { err = 1; goto err; } - array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1)); - zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + + RANGE_CHECK_LONG_INIT_ARRAY(high, low); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (; low <= high; low += lstep) { - Z_LVAL(tmp) = (zend_long)low; + for (i = 0; i < size; ++i) { + Z_LVAL(tmp) = low + (i * lstep); ZEND_HASH_FILL_ADD(&tmp); } } ZEND_HASH_FILL_END(); } else { array_init(return_value); - Z_LVAL(tmp) = (zend_long)low; + Z_LVAL(tmp) = low; zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } } @@ -1893,6 +2324,9 @@ err: } /* }}} */ +#undef RANGE_CHECK_DOUBLE_INIT_ARRAY +#undef RANGE_CHECK_LONG_INIT_ARRAY + static void php_array_data_shuffle(zval *array) /* {{{ */ { uint32_t idx, j, n_elems; @@ -2611,6 +3045,10 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */ if (Z_TYPE_P(dest_zval) == IS_NULL) { convert_to_array_ex(dest_zval); add_next_index_null(dest_zval); + } else if (Z_TYPE_P(dest_zval) == IS_ARRAY) { + if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > Z_ARRVAL_P(dest_zval)->nNumUsed)) { + Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed; + } } else { convert_to_array_ex(dest_zval); } @@ -2661,15 +3099,20 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */ zend_string *string_key; ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { - if (string_key) { - if (Z_REFCOUNTED_P(src_entry)) { + if (Z_REFCOUNTED_P(src_entry)) { + if (UNEXPECTED(Z_ISREF_P(src_entry)) + && UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) { + ZVAL_UNREF(src_entry); + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + } else { Z_ADDREF_P(src_entry); } + } + if (string_key) { zend_hash_update(dest, string_key, src_entry); } else { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } zend_hash_next_index_insert_new(dest, src_entry); } } ZEND_HASH_FOREACH_END(); @@ -2796,15 +3239,19 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE src = Z_ARRVAL_P(arg); dest = Z_ARRVAL_P(return_value); ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) { - if (string_key) { - if (Z_REFCOUNTED_P(src_entry)) { + if (Z_REFCOUNTED_P(src_entry)) { + if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) { + src_entry = Z_REFVAL_P(src_entry); + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + } else { Z_ADDREF_P(src_entry); } + } + if (string_key) { zend_hash_add_new(dest, string_key, src_entry); } else { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } zend_hash_index_add_new(dest, idx, src_entry); } } ZEND_HASH_FOREACH_END(); @@ -2833,15 +3280,19 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE src = Z_ARRVAL_P(arg); dest = Z_ARRVAL_P(return_value); ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { - if (string_key) { - if (Z_REFCOUNTED_P(src_entry)) { + if (Z_REFCOUNTED_P(src_entry)) { + if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) { + src_entry = Z_REFVAL_P(src_entry); + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + } else { Z_ADDREF_P(src_entry); } + } + if (string_key) { zend_hash_add_new(dest, string_key, src_entry); } else { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } zend_hash_next_index_insert_new(dest, src_entry); } } ZEND_HASH_FOREACH_END(); @@ -2926,6 +3377,7 @@ PHP_FUNCTION(array_keys) if (strict) { ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + ZVAL_DEREF(entry); if (fast_is_identical_function(search_value, entry)) { if (str_idx) { ZVAL_STR_COPY(&new_val, str_idx); @@ -2949,6 +3401,9 @@ PHP_FUNCTION(array_keys) } } else { array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); + if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { + return; + } zend_hash_real_init(Z_ARRVAL_P(return_value), 1); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { /* Go through input array and add keys to the return array */ @@ -2984,6 +3439,11 @@ PHP_FUNCTION(array_values) /* Initialize return array */ array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); + + if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { + return; + } + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); /* Go through input array and add values to the return array */ @@ -3073,20 +3533,29 @@ static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) zval *prop = NULL; if (Z_TYPE_P(data) == IS_OBJECT) { - zend_string *key = zval_get_string(name); + if (!Z_OBJ_HANDLER_P(data, has_property) || !Z_OBJ_HANDLER_P(data, read_property)) { + return NULL; + } - if (!Z_OBJ_HANDLER_P(data, has_property) || Z_OBJ_HANDLER_P(data, has_property)(data, name, 1, NULL)) { - prop = zend_read_property(Z_OBJCE_P(data), data, ZSTR_VAL(key), ZSTR_LEN(key), 1, rv); + /* The has_property check is first performed in "exists" mode (which returns true for + * properties that are null but exist) and then in "has" mode to handle objects that + * implement __isset (which is not called in "exists" mode). */ + if (Z_OBJ_HANDLER_P(data, has_property)(data, name, 2, NULL) + || Z_OBJ_HANDLER_P(data, has_property)(data, name, 0, NULL)) { + prop = Z_OBJ_HANDLER_P(data, read_property)(data, name, BP_VAR_R, NULL, rv); } - zend_string_release(key); } else if (Z_TYPE_P(data) == IS_ARRAY) { if (Z_TYPE_P(name) == IS_STRING) { - prop = zend_hash_find(Z_ARRVAL_P(data), Z_STR_P(name)); + prop = zend_symtable_find(Z_ARRVAL_P(data), Z_STR_P(name)); } else if (Z_TYPE_P(name) == IS_LONG) { prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name)); } } + if (prop) { + ZVAL_DEREF(prop); + } + return prop; } @@ -3340,19 +3809,22 @@ PHP_FUNCTION(array_unique) struct bucketindex *arTmp, *cmpdata, *lastkept; unsigned int i; zend_long sort_type = PHP_SORT_STRING; + compare_func_t cmp; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &sort_type) == FAILURE) { return; } - php_set_compare_func(sort_type); + cmp = php_get_data_compare_func(sort_type, 0); - RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array))); if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */ + ZVAL_COPY(return_value, array); return; } + RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array))); + /* create and sort array with pointers to the target_hash buckets */ arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT); if (!arTmp) { @@ -3369,11 +3841,11 @@ PHP_FUNCTION(array_unique) } ZVAL_UNDEF(&arTmp[i].b.val); zend_sort((void *) arTmp, i, sizeof(struct bucketindex), - php_array_data_compare, (swap_func_t)array_bucketindex_swap); + cmp, (swap_func_t)array_bucketindex_swap); /* go through the sorted array and delete duplicates from the copy */ lastkept = arTmp; for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) { - if (php_array_data_compare(lastkept, cmpdata)) { + if (cmp(lastkept, cmpdata)) { lastkept = cmpdata; } else { if (lastkept->i > cmpdata->i) { @@ -3399,24 +3871,7 @@ PHP_FUNCTION(array_unique) static int zval_compare(zval *first, zval *second) /* {{{ */ { - zval result; - - if (Z_TYPE_P(first) == IS_INDIRECT) { - first = Z_INDIRECT_P(first); - } - if (Z_TYPE_P(second) == IS_INDIRECT) { - second = Z_INDIRECT_P(second); - } - if (string_compare_function(&result, first, second) == FAILURE) { - return 0; - } - - if (Z_TYPE(result) == IS_DOUBLE) { - return ZEND_NORMALIZE_BOOL(Z_DVAL(result)); - } - - convert_to_long(&result); - return ZEND_NORMALIZE_BOOL(Z_LVAL(result)); + return string_compare_function(first, second); } /* }}} */ @@ -3425,13 +3880,6 @@ static int zval_user_compare(zval *a, zval *b) /* {{{ */ zval args[2]; zval retval; - if (Z_TYPE_P(a) == IS_INDIRECT) { - a = Z_INDIRECT_P(a); - } - if (Z_TYPE_P(b) == IS_INDIRECT) { - b = Z_INDIRECT_P(b); - } - ZVAL_COPY_VALUE(&args[0], a); ZVAL_COPY_VALUE(&args[1], b); @@ -3502,6 +3950,10 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa p = Z_ARRVAL(args[0])->arData + idx; val = &p->val; if (Z_TYPE_P(val) == IS_UNDEF) continue; + if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) { + val = Z_INDIRECT_P(val); + if (Z_TYPE_P(val) == IS_UNDEF) continue; + } if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { ZVAL_UNREF(val); } @@ -3525,7 +3977,7 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa } else { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_find(Z_ARRVAL(args[i]), p->key)) == NULL || + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) == NULL || (intersect_data_compare_func && intersect_data_compare_func(val, data) != 0) ) { @@ -3563,13 +4015,13 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int int (*intersect_data_compare_func)(const void *, const void *); if (behavior == INTERSECT_NORMAL) { - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) { /* array_intersect() */ req_args = 2; param_spec = "+"; - intersect_data_compare_func = php_array_data_compare; + intersect_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == INTERSECT_COMP_DATA_USER) { /* array_uintersect() */ req_args = 3; @@ -3594,19 +4046,19 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former * no comparison of the data is done (part of INTERSECT_ASSOC) */ - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) { /* array_intersect_assoc() or array_intersect_key() */ req_args = 2; param_spec = "+"; - intersect_key_compare_func = php_array_key_compare; - intersect_data_compare_func = php_array_data_compare; + intersect_key_compare_func = php_array_key_compare_string; + intersect_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) { /* array_uintersect_assoc() */ req_args = 3; param_spec = "+f"; - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; intersect_data_compare_func = php_array_user_compare; fci_data = &fci1; fci_data_cache = &fci1_cache; @@ -3615,7 +4067,7 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int req_args = 3; param_spec = "+f"; intersect_key_compare_func = php_array_user_key_compare; - intersect_data_compare_func = php_array_data_compare; + intersect_data_compare_func = php_array_data_compare_string; fci_key = &fci1; fci_key_cache = &fci1_cache; } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) { @@ -3652,7 +4104,6 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int /* for each argument, create and sort list with pointers to the hash buckets */ lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); - php_set_compare_func(PHP_SORT_STRING); if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) { BG(user_compare_fci) = *fci_data; @@ -3922,6 +4373,10 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty p = Z_ARRVAL(args[0])->arData + idx; val = &p->val; if (Z_TYPE_P(val) == IS_UNDEF) continue; + if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) { + val = Z_INDIRECT_P(val); + if (Z_TYPE_P(val) == IS_UNDEF) continue; + } if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { ZVAL_UNREF(val); } @@ -3945,7 +4400,7 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty } else { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_find(Z_ARRVAL(args[i]), p->key)) != NULL && + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) != NULL && (!diff_data_compare_func || diff_data_compare_func(val, data) == 0) ) { @@ -3983,13 +4438,13 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ int (*diff_data_compare_func)(const void *, const void *); if (behavior == DIFF_NORMAL) { - diff_key_compare_func = php_array_key_compare; + diff_key_compare_func = php_array_key_compare_string; if (data_compare_type == DIFF_COMP_DATA_INTERNAL) { /* array_diff */ req_args = 2; param_spec = "+"; - diff_data_compare_func = php_array_data_compare; + diff_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == DIFF_COMP_DATA_USER) { /* array_udiff */ req_args = 3; @@ -4019,13 +4474,13 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* array_diff_assoc() or array_diff_key() */ req_args = 2; param_spec = "+"; - diff_key_compare_func = php_array_key_compare; - diff_data_compare_func = php_array_data_compare; + diff_key_compare_func = php_array_key_compare_string; + diff_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) { /* array_udiff_assoc() */ req_args = 3; param_spec = "+f"; - diff_key_compare_func = php_array_key_compare; + diff_key_compare_func = php_array_key_compare_string; diff_data_compare_func = php_array_user_compare; fci_data = &fci1; fci_data_cache = &fci1_cache; @@ -4034,7 +4489,7 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ req_args = 3; param_spec = "+f"; diff_key_compare_func = php_array_user_key_compare; - diff_data_compare_func = php_array_data_compare; + diff_data_compare_func = php_array_data_compare_string; fci_key = &fci1; fci_key_cache = &fci1_cache; } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) { @@ -4071,7 +4526,6 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* for each argument, create and sort list with pointers to the hash buckets */ lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); - php_set_compare_func(PHP_SORT_STRING); if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) { BG(user_compare_fci) = *fci_data; @@ -4366,15 +4820,10 @@ PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */ Bucket *bb = *(Bucket **)b; int r; zend_long result; - zval temp; r = 0; do { - - php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r]); - - ARRAYG(compare_func)(&temp, &ab[r].val, &bb[r].val); - result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp); + result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]); if (result != 0) { return result > 0 ? 1 : -1; } @@ -4385,10 +4834,9 @@ PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */ } /* }}} */ -#define MULTISORT_ABORT \ - for (k = 0; k < MULTISORT_LAST; k++) \ - efree(ARRAYG(multisort_flags)[k]); \ - efree(arrays); \ +#define MULTISORT_ABORT \ + efree(ARRAYG(multisort_func)); \ + efree(arrays); \ RETURN_FALSE; static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ { @@ -4428,8 +4876,8 @@ PHP_FUNCTION(array_multisort) arrays = (zval **)ecalloc(argc, sizeof(zval *)); for (i = 0; i < MULTISORT_LAST; i++) { parse_state[i] = 0; - ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int)); } + ARRAYG(multisort_func) = (compare_func_t*)ecalloc(argc, sizeof(compare_func_t)); /* Here we go through the input arguments and parse them. Each one can * be either an array or a sort flag which follows an array. If not @@ -4445,8 +4893,7 @@ PHP_FUNCTION(array_multisort) /* We see the next array, so we update the sort flags of * the previous array and reset the sort flags. */ if (i > 0) { - ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; - ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; + ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC); sort_order = PHP_SORT_ASC; sort_type = PHP_SORT_REGULAR; } @@ -4463,7 +4910,7 @@ PHP_FUNCTION(array_multisort) /* flag allowed here */ if (parse_state[MULTISORT_ORDER] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ - sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? -1 : 1; + sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC; parse_state[MULTISORT_ORDER] = 0; } else { php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); @@ -4501,8 +4948,7 @@ PHP_FUNCTION(array_multisort) } } /* Take care of the last array sort flags. */ - ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; - ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; + ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC); /* Make sure the arrays are of the same size. */ array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0])); @@ -4515,9 +4961,7 @@ PHP_FUNCTION(array_multisort) /* If all arrays are empty we don't need to do anything. */ if (array_size < 1) { - for (k = 0; k < MULTISORT_LAST; k++) { - efree(ARRAYG(multisort_flags)[k]); - } + efree(ARRAYG(multisort_func)); efree(arrays); RETURN_TRUE; } @@ -4578,9 +5022,7 @@ PHP_FUNCTION(array_multisort) efree(indirect[i]); } efree(indirect); - for (k = 0; k < MULTISORT_LAST; k++) { - efree(ARRAYG(multisort_flags)[k]); - } + efree(ARRAYG(multisort_func)); efree(arrays); RETURN_TRUE; } @@ -4807,7 +5249,7 @@ PHP_FUNCTION(array_filter) } } - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) { + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, string_key, operand) { if (have_callback) { if (use_type) { /* Set up the key */ @@ -5041,7 +5483,7 @@ PHP_FUNCTION(array_key_exists) switch (Z_TYPE_P(key)) { case IS_STRING: - if (zend_symtable_exists(array, Z_STR_P(key))) { + if (zend_symtable_exists_ind(array, Z_STR_P(key))) { RETURN_TRUE; } RETURN_FALSE; @@ -5051,7 +5493,7 @@ PHP_FUNCTION(array_key_exists) } RETURN_FALSE; case IS_NULL: - if (zend_hash_exists(array, ZSTR_EMPTY_ALLOC())) { + if (zend_hash_exists_ind(array, ZSTR_EMPTY_ALLOC())) { RETURN_TRUE; } RETURN_FALSE; @@ -5132,17 +5574,17 @@ PHP_FUNCTION(array_chunk) Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */ PHP_FUNCTION(array_combine) { - zval *values, *keys; + HashTable *values, *keys; uint32_t pos_values = 0; zval *entry_keys, *entry_values; int num_keys, num_values; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "aa", &keys, &values) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "hh", &keys, &values) == FAILURE) { return; } - num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); - num_values = zend_hash_num_elements(Z_ARRVAL_P(values)); + num_keys = zend_hash_num_elements(keys); + num_values = zend_hash_num_elements(values); if (num_keys != num_values) { php_error_docref(NULL, E_WARNING, "Both parameters should have an equal number of elements"); @@ -5155,12 +5597,12 @@ PHP_FUNCTION(array_combine) return; } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry_keys) { + ZEND_HASH_FOREACH_VAL(keys, entry_keys) { while (1) { - if (pos_values >= Z_ARRVAL_P(values)->nNumUsed) { + if (pos_values >= values->nNumUsed) { break; - } else if (Z_TYPE(Z_ARRVAL_P(values)->arData[pos_values].val) != IS_UNDEF) { - entry_values = &Z_ARRVAL_P(values)->arData[pos_values].val; + } else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) { + entry_values = &values->arData[pos_values].val; if (Z_TYPE_P(entry_keys) == IS_LONG) { entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry_keys), entry_values); |