diff options
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r-- | ext/standard/array.c | 194 |
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); } /* }}} */ |