diff --git a/ext/standard/array.c b/ext/standard/array.c index 0b184efc3998..504609f4c216 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4275,15 +4275,20 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET ret = &args[0]; } if (ret) { - if (HT_IS_PACKED(Z_ARRVAL_P(ret))) { - if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) { + HashTable *ht = Z_ARRVAL_P(ret); + /* ZEND_LONG_MIN means no integer key yet, the next append uses index 0. */ + zend_long next_free = ht->nNextFreeElement == ZEND_LONG_MIN ? 0 : ht->nNextFreeElement; + + /* array_merge() renumbers integer keys from 0, so the next free index must match. */ + if (HT_IS_PACKED(ht)) { + if (HT_IS_WITHOUT_HOLES(ht) && next_free == (zend_long) ht->nNumOfElements) { RETURN_COPY(ret); } - } else { + } else if (next_free == 0) { bool copy = true; zend_string *string_key; - ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) { + ZEND_HASH_MAP_FOREACH_STR_KEY(ht, string_key) { if (!string_key) { copy = false; break; diff --git a/ext/standard/tests/array/array_merge_empty_array_result.phpt b/ext/standard/tests/array/array_merge_empty_array_result.phpt new file mode 100644 index 000000000000..bc480c884b7b --- /dev/null +++ b/ext/standard/tests/array/array_merge_empty_array_result.phpt @@ -0,0 +1,34 @@ +--TEST-- +array_merge() with an empty argument returns an array with a fresh next free index +--FILE-- + 1]; +$b[] = 2; +unset($b[0]); +$r = array_merge([], $b); +$r[] = 3; +var_dump($r); +?> +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(4) +} +array(2) { + ["a"]=> + int(1) + [0]=> + int(3) +}