Skip to content

If the first numeric index of an array is negative let the next one stay negative #2383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 19 additions & 7 deletions Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions ext/standard/tests/array/array_fill_variation1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions ext/standard/tests/array/array_fill_variation1_64bit.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/array/bug67693.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions ext/standard/tests/array/count_recursive.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions main/php_variables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down