diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e6a34371a3a33..9cd55b58806ec 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7048,7 +7048,9 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */ break; } } else { - if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), value)) { + /* Bail out of CT eval in case the deprecation notice would be emitted so it may be handled in RT */ + if (HASH_ADD_NEXT_EMITS_DEPRECATED(Z_ARRVAL_P(result)) || + !zend_hash_next_index_insert(Z_ARRVAL_P(result), value)) { zval_ptr_dtor_nogc(value); zval_ptr_dtor(result); return 0; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index f5670dd3709fa..c9a14118238ab 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -181,7 +181,7 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize ht->nNumUsed = 0; ht->nNumOfElements = 0; ht->nInternalPointer = HT_INVALID_IDX; - ht->nNextFreeElement = 0; + ht->nNextFreeElement = ZEND_LONG_MIN; ht->pDestructor = pDestructor; ht->nTableSize = zend_hash_check_size(nSize); } @@ -789,6 +789,18 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, IS_CONSISTENT(ht); HT_ASSERT_RC1(ht); + /* Removing this block will end the deprecation phase and allow the new behavior */ + if (UNEXPECTED(flag & HASH_ADD_NEXT + && HASH_ADD_NEXT_EMITS_DEPRECATED(ht))) { + zend_error(E_DEPRECATED, "In the next major version of PHP the implicit keys of this array will start from " ZEND_LONG_FMT " instead of 0", + ht->nNextFreeElement); + h = 0; + } + + if (h == ZEND_LONG_MIN && flag & HASH_ADD_NEXT) { + h = 0; + } + if (UNEXPECTED(!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED))) { CHECK_INIT(ht, h < ht->nTableSize); if (h < ht->nTableSize) { @@ -841,7 +853,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } zend_hash_iterators_update(ht, HT_INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } p->h = h; p->key = NULL; @@ -863,7 +875,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } ZVAL_COPY_VALUE(&p->val, pData); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } return &p->val; } @@ -879,7 +891,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } p = ht->arData + idx; p->h = h; @@ -1471,7 +1483,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) } ht->nNumUsed = 0; ht->nNumOfElements = 0; - ht->nNextFreeElement = 0; + ht->nNextFreeElement = ZEND_LONG_MIN; ht->nInternalPointer = HT_INVALID_IDX; } @@ -1510,7 +1522,7 @@ ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht) } ht->nNumUsed = 0; ht->nNumOfElements = 0; - ht->nNextFreeElement = 0; + ht->nNextFreeElement = ZEND_LONG_MIN; ht->nInternalPointer = HT_INVALID_IDX; } @@ -1837,7 +1849,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nTableMask = HT_MIN_MASK; target->nNumUsed = 0; target->nNumOfElements = 0; - target->nNextFreeElement = 0; + target->nNextFreeElement = ZEND_LONG_MIN; target->nInternalPointer = HT_INVALID_IDX; HT_SET_DATA_ADDR(target, &uninitialized_bucket); } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index af9c21a7ecc20..4339455a71126 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -35,6 +35,9 @@ #define HASH_ADD_NEW (1<<3) #define HASH_ADD_NEXT (1<<4) +#define HASH_ADD_NEXT_EMITS_DEPRECATED(ht) \ + ((ht)->nNextFreeElement > ZEND_LONG_MIN && (ht)->nNextFreeElement < 0) + #define HASH_FLAG_CONSISTENCY ((1<<0) | (1<<1)) #define HASH_FLAG_PACKED (1<<2) #define HASH_FLAG_INITIALIZED (1<<3) diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 07fdeee7089b0..8418254c08feb 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -475,7 +475,7 @@ static inline int ct_eval_del_array_elem(zval *result, zval *key) { static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) { if (!key) { SEPARATE_ARRAY(result); - if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) { + if (!HASH_ADD_NEXT_EMITS_DEPRECATED(Z_ARR_P(result)) && (value = zend_hash_next_index_insert(Z_ARR_P(result), value))) { Z_TRY_ADDREF_P(value); return SUCCESS; } diff --git a/ext/standard/array.c b/ext/standard/array.c index 50ea6802ae301..26c67060698db 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3275,7 +3275,7 @@ PHP_FUNCTION(array_pop) ZVAL_DEREF(val); ZVAL_COPY(return_value, val); - if (!p->key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) { + if (!p->key && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) { Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1; } diff --git a/ext/standard/tests/array/array_fill_variation1.phpt b/ext/standard/tests/array/array_fill_variation1.phpt index 684949fe95905..9001e998b01b2 100644 --- a/ext/standard/tests/array/array_fill_variation1.phpt +++ b/ext/standard/tests/array/array_fill_variation1.phpt @@ -117,6 +117,8 @@ array(2) { int(100) } -- Iteration 2 -- + +Deprecated: In the next major version of PHP the implicit keys of this array will start from -9 instead of 0 in %s array(2) { [-10]=> int(100) diff --git a/ext/standard/tests/array/array_fill_variation1_64bit.phpt b/ext/standard/tests/array/array_fill_variation1_64bit.phpt index 6cbec86f60843..2283d10c17856 100644 --- a/ext/standard/tests/array/array_fill_variation1_64bit.phpt +++ b/ext/standard/tests/array/array_fill_variation1_64bit.phpt @@ -117,6 +117,8 @@ array(2) { int(100) } -- Iteration 2 -- + +Deprecated: In the next major version of PHP the implicit keys of this array will start from -9 instead of 0 in %s array(2) { [-10]=> int(100) diff --git a/ext/standard/tests/array/bug67693.phpt b/ext/standard/tests/array/bug67693.phpt index 516436c511388..bc8236fefee6e 100644 --- a/ext/standard/tests/array/bug67693.phpt +++ b/ext/standard/tests/array/bug67693.phpt @@ -14,7 +14,8 @@ var_dump($array); echo"\nDone"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: In the next major version of PHP the implicit keys of this array will start from -1 instead of 0 in %s array(2) { [0]=> int(0) diff --git a/ext/standard/tests/array/count_recursive.phpt b/ext/standard/tests/array/count_recursive.phpt index b903d8b189236..a925e3cacedbb 100644 --- a/ext/standard/tests/array/count_recursive.phpt +++ b/ext/standard/tests/array/count_recursive.phpt @@ -161,6 +161,8 @@ COUNT_NORMAL: should be 3, is 3 COUNT_RECURSIVE: should be 13, is 13 *** Testing possible variations of count() function on arrays *** +Deprecated: In the next major version of PHP the implicit keys of this array will start from -1 instead of 0 in %s + -- Iteration 0 -- COUNT_NORMAL is 0 COUNT_RECURSIVE is 0 diff --git a/main/php_variables.c b/main/php_variables.c index 9e7f185bd9447..4bba9e9ee7557 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -245,6 +245,10 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars } else { plain_var: if (!index) { + /* Avoid the deprecation notice during globals registration */ + if (HASH_ADD_NEXT_EMITS_DEPRECATED(symtable1)) { + symtable1->nNextFreeElement = 0; + } if (zend_hash_next_index_insert(symtable1, val) == NULL) { zval_ptr_dtor(val); }