summaryrefslogtreecommitdiff
path: root/ext/standard/array.c
diff options
context:
space:
mode:
authorJakub Zelenka <bukka@php.net>2016-06-19 17:05:48 +0100
committerJakub Zelenka <bukka@php.net>2016-06-19 17:05:48 +0100
commite63a8540a60e95aa5bd8e269add1b02afcc1b79b (patch)
treeb83a144eec24cc81adab0b9a778f7a730d8df79e /ext/standard/array.c
parent7a4cc73641bb3eb878f7184bcbd026ee663cf2a9 (diff)
parent53071e647049f099f7f7a0771ddb63fc2cdd621c (diff)
downloadphp-git-e63a8540a60e95aa5bd8e269add1b02afcc1b79b.tar.gz
Merge branch 'openssl_error_store' into openssl_aead
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r--ext/standard/array.c205
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;