Skip to content
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
19 changes: 15 additions & 4 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -4275,15 +4275,26 @@ 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);
/* An array that never had an integer key keeps the ZEND_LONG_MIN
* sentinel; appending to it uses index 0. */
zend_long next_free = ht->nNextFreeElement == ZEND_LONG_MIN ? 0 : ht->nNextFreeElement;

/* array_merge() builds a new array: integer keys are renumbered from
* zero, the next free index is reset and the internal pointer is
* rewound to the first element. The argument may be returned as is
* only when it already behaves like the result of that code path. */
if (HT_IS_PACKED(ht)) {
if (HT_IS_WITHOUT_HOLES(ht) &&
ht->nInternalPointer == 0 &&
next_free == (zend_long) ht->nNumOfElements) {
RETURN_COPY(ret);
}
} else {
} else if (next_free == 0 && ht->nInternalPointer == 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;
Expand Down
42 changes: 42 additions & 0 deletions ext/standard/tests/array/array_merge_empty_array_result.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
array_merge() with an empty argument returns an array with a fresh next free index and internal pointer
--FILE--
<?php
// array_merge() with an empty argument returns the other argument itself. The
// result must still behave like a fresh array: integer keys renumbered from 0,
// next free index reset and internal pointer on the first element.

$a = [1, 2, 3];
unset($a[2]);
$r = array_merge($a, []);
$r[] = 4;
var_dump($r);

$b = ['a' => 1];
$b[] = 2;
unset($b[0]);
$r = array_merge([], $b);
$r[] = 3;
var_dump($r);

$c = [10, 20, 30];
next($c);
next($c);
var_dump(current(array_merge([], $c)));
?>
--EXPECT--
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(4)
}
array(2) {
["a"]=>
int(1)
[0]=>
int(3)
}
int(10)
Loading