diff options
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r-- | ext/standard/array.c | 205 |
1 files changed, 134 insertions, 71 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c index 3e9ecdfd46..67aec057b8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1029,37 +1029,30 @@ static int php_array_user_compare(const void *a, const void *b) /* {{{ */ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */ { zval *array; - zend_refcounted *arr; + 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 by comparing the - * zend_array pointer. The result of sorting in such case is undefined and - * the function returns FALSE. - */ - Z_ADDREF_P(array); - arr = Z_COUNTED_P(array); + arr = Z_ARR_P(array); + if (zend_hash_num_elements(arr) == 0) { + PHP_ARRAY_CMP_FUNC_RESTORE(); + RETURN_TRUE; + } - retval = zend_hash_sort(Z_ARRVAL_P(array), compare_func, renumber) != FAILURE; + /* Copy array, so the in-place modifications will not be visible to the callback function */ + arr = zend_array_dup(arr); - if (arr != Z_COUNTED_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 = 0; - } else { - Z_DELREF_P(array); - } + 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); @@ -1620,7 +1613,6 @@ static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) } } ZEND_HASH_FOREACH_END(); } else { - ZVAL_DEREF(value); if (Z_TYPE_P(value) == IS_LONG) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { if (fast_equal_check_long(value, entry)) { @@ -1772,6 +1764,7 @@ PHP_FUNCTION(extract) zend_ulong num_key; int var_exists, count = 0; int extract_refs = 0; + int exception_thrown = 0; zend_array *symbol_table; zval var_array; @@ -1812,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) { @@ -1850,9 +1847,6 @@ 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; - } ZVAL_STR_COPY(&final_name, var_name); break; @@ -1893,6 +1887,15 @@ 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 (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) { + if (!exception_thrown) { + exception_thrown = 1; + zend_throw_error(NULL, "Cannot re-assign $this"); + } + zval_dtor(&final_name); + continue; + } if (extract_refs) { ZVAL_MAKE_REF(entry); @@ -1973,8 +1976,11 @@ PHP_FUNCTION(compact) return; } - symbol_table = zend_rebuild_symbol_table(); + if (zend_forbid_dynamic_call("compact()") == FAILURE) { + return; + } + symbol_table = zend_rebuild_symbol_table(); if (UNEXPECTED(symbol_table == NULL)) { return; } @@ -2001,34 +2007,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; } } /* }}} */ @@ -2066,7 +2111,7 @@ PHP_FUNCTION(array_fill_keys) 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)__calc_size; \ + size = (uint32_t)round(__calc_size); \ array_init_size(return_value, size); \ zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \ } while (0) @@ -2178,7 +2223,7 @@ 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; + double low, high, element; uint32_t i, size; double_str: low = zval_get_double(zlow); @@ -2199,8 +2244,8 @@ double_str: RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (i = 0; i < size; ++i) { - Z_DVAL(tmp) = low - (i * step); + 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(); @@ -2213,8 +2258,8 @@ double_str: RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (i = 0; i < size; ++i) { - Z_DVAL(tmp) = low + (i * step); + 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(); @@ -3057,15 +3102,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(); @@ -3192,15 +3242,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(); @@ -3229,15 +3283,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(); @@ -3478,15 +3536,20 @@ 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)); } @@ -5423,7 +5486,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; @@ -5433,7 +5496,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; |