summaryrefslogtreecommitdiff
path: root/ext/standard/array.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r--ext/standard/array.c194
1 files changed, 119 insertions, 75 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c
index e5c38f2906..ccf3ceb0f2 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -46,9 +46,8 @@
#include "php_string.h"
#include "php_rand.h"
#include "zend_smart_str.h"
-#ifdef HAVE_SPL
+#include "zend_bitset.h"
#include "ext/spl/spl_array.h"
-#endif
/* {{{ defines */
#define EXTR_OVERWRITE 0
@@ -821,9 +820,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);
@@ -831,7 +828,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);
@@ -841,7 +837,6 @@ PHP_FUNCTION(count)
}
return;
}
-#endif
}
default:
RETURN_LONG(1);
@@ -1621,7 +1616,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)) {
@@ -1773,6 +1767,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;
@@ -1813,6 +1808,10 @@ PHP_FUNCTION(extract)
}
}
+ if (zend_forbid_dynamic_call("extract()") == FAILURE) {
+ return;
+ }
+
symbol_table = zend_rebuild_symbol_table();
#if 0
if (!symbol_table) {
@@ -1851,9 +1850,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;
@@ -1894,6 +1890,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);
@@ -1974,8 +1979,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;
}
@@ -2114,7 +2122,7 @@ PHP_FUNCTION(array_fill_keys)
#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); \
+ php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
RETURN_FALSE; \
} \
size = (uint32_t)(__calc_size + 1); \
@@ -2281,7 +2289,7 @@ long_str:
Z_TYPE_INFO(tmp) = IS_LONG;
if (low > high) { /* Negative steps */
- if (low - high < lstep) {
+ if ((zend_ulong)(low - high) < lstep) {
err = 1;
goto err;
}
@@ -2295,7 +2303,7 @@ long_str:
}
} ZEND_HASH_FILL_END();
} else if (high > low) { /* Positive steps */
- if (high - low < lstep) {
+ if ((zend_ulong)(high - low) < lstep) {
err = 1;
goto err;
}
@@ -2331,7 +2339,7 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
Bucket *p, temp;
HashTable *hash;
zend_long rnd_idx;
- uint32_t n_left;
+ zend_long n_left;
n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
@@ -2390,7 +2398,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
}
}
}
- HANDLE_BLOCK_INTERRUPTIONS();
hash->nNumUsed = n_elems;
hash->nInternalPointer = 0;
@@ -2406,7 +2413,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
if (!(hash->u.flags & HASH_FLAG_PACKED)) {
zend_hash_to_packed(hash);
}
- HANDLE_UNBLOCK_INTERRUPTIONS();
}
/* }}} */
@@ -2426,12 +2432,12 @@ PHP_FUNCTION(shuffle)
}
/* }}} */
-static void php_splice(HashTable *in_hash, int offset, int length, HashTable *replace, HashTable *removed) /* {{{ */
+static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
{
HashTable out_hash; /* Output hashtable */
- int num_in, /* Number of entries in the input hashtable */
- pos; /* Current position in the hashtable */
- uint idx;
+ zend_long num_in; /* Number of entries in the input hashtable */
+ zend_long pos; /* Current position in the hashtable */
+ uint32_t idx;
Bucket *p; /* Pointer to hash bucket */
zval *entry; /* Hash entry */
uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
@@ -2470,7 +2476,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
- if (idx != pos) {
+ if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
@@ -2540,7 +2546,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
- if (idx != pos) {
+ if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
@@ -2899,7 +2905,7 @@ PHP_FUNCTION(array_splice)
}
/* Perform splice */
- php_splice(Z_ARRVAL_P(array), (int)offset, (int)length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
+ php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
}
/* }}} */
@@ -3044,7 +3050,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
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)) {
+ if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > (zend_long)Z_ARRVAL_P(dest_zval)->nNumUsed)) {
Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed;
}
} else {
@@ -3237,18 +3243,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 (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(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();
@@ -3277,18 +3284,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 (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(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();
@@ -4949,7 +4957,7 @@ PHP_FUNCTION(array_multisort)
/* Make sure the arrays are of the same size. */
array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
for (i = 0; i < num_arrays; i++) {
- if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
+ if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent");
MULTISORT_ABORT;
}
@@ -4984,10 +4992,9 @@ PHP_FUNCTION(array_multisort)
}
/* Do the actual sort magic - bada-bim, bada-boom. */
- zend_qsort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
+ zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
- HANDLE_BLOCK_INTERRUPTIONS();
for (i = 0; i < num_arrays; i++) {
int repack;
@@ -5011,7 +5018,6 @@ PHP_FUNCTION(array_multisort)
zend_hash_rehash(hash);
}
}
- HANDLE_UNBLOCK_INTERRUPTIONS();
/* Clean up. */
for (i = 0; i < array_size; i++) {
@@ -5029,10 +5035,14 @@ PHP_FUNCTION(array_multisort)
PHP_FUNCTION(array_rand)
{
zval *input;
- zend_long randval, num_req = 1;
- int num_avail;
+ zend_long num_req = 1;
zend_string *string_key;
zend_ulong num_key;
+ int i;
+ int num_avail;
+ zend_bitset bitset;
+ int negative_bitset = 0;
+ uint32_t bitset_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) {
return;
@@ -5040,46 +5050,80 @@ PHP_FUNCTION(array_rand)
num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
- if (ZEND_NUM_ARGS() > 1) {
- if (num_req <= 0 || num_req > num_avail) {
- php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
- return;
+ if (num_avail == 0) {
+ php_error_docref(NULL, E_WARNING, "Array is empty");
+ return;
+ }
+
+ if (num_req == 1) {
+ HashTable *ht = Z_ARRVAL_P(input);
+
+ /* Compact the hashtable if less than 3/4 of elements are used */
+ if (num_avail < ht->nNumUsed - (ht->nNumUsed>>2)) {
+ if (ht->u.flags & HASH_FLAG_PACKED) {
+ zend_hash_packed_to_hash(ht);
+ } else {
+ zend_hash_rehash(ht);
+ }
}
+
+ /* Sample random buckets until we hit one that is not empty.
+ * The worst case probability of hitting an empty element is 1-3/4. The worst case
+ * probability of hitting N empty elements in a row is (1-3/4)**N.
+ * For N=5 this becomes smaller than 0.1%. */
+ do {
+ zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
+ Bucket *bucket = &ht->arData[randval];
+ if (!Z_ISUNDEF(bucket->val)) {
+ if (bucket->key) {
+ RETURN_STR_COPY(bucket->key);
+ } else {
+ RETURN_LONG(bucket->h);
+ }
+ }
+ } while (1);
+ }
+
+ if (num_req <= 0 || num_req > num_avail) {
+ php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
+ return;
}
/* Make the return value an array only if we need to pass back more than one result. */
- if (num_req > 1) {
- array_init_size(return_value, (uint32_t)num_req);
+ array_init_size(return_value, (uint32_t)num_req);
+ if (num_req > (num_avail >> 1)) {
+ negative_bitset = 1;
+ num_req = num_avail - num_req;
}
- /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
- ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
- if (!num_req) {
- break;
- }
+ ALLOCA_FLAG(use_heap);
+ bitset_len = zend_bitset_len(num_avail);
+ bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
+ zend_bitset_clear(bitset, bitset_len);
- randval = php_rand();
+ i = num_req;
+ while (i) {
+ zend_long randval = php_mt_rand_range(0, num_avail - 1);
+ if (!zend_bitset_in(bitset, randval)) {
+ zend_bitset_incl(bitset, randval);
+ i--;
+ }
+ }
- if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
- /* If we are returning a single result, just do it. */
- if (Z_TYPE_P(return_value) != IS_ARRAY) {
- if (string_key) {
- RETURN_STR_COPY(string_key);
- } else {
- RETURN_LONG(num_key);
- }
+ /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
+ i = 0;
+ ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
+ if (zend_bitset_in(bitset, i) ^ negative_bitset) {
+ if (string_key) {
+ add_next_index_str(return_value, zend_string_copy(string_key));
} else {
- /* Append the result to the return value. */
- if (string_key) {
- add_next_index_str(return_value, zend_string_copy(string_key));
- } else {
- add_next_index_long(return_value, num_key);
- }
+ add_next_index_long(return_value, num_key);
}
- num_req--;
}
- num_avail--;
+ i++;
} ZEND_HASH_FOREACH_END();
+
+ free_alloca(bitset, use_heap);
}
/* }}} */