234

I have some code that appears to merge the data from two arrays using +=, but it doesn't include all of the elements in the element. How does it work?

Example:

$test = array('hi');
$test += array('test', 'oh');
var_dump($test);

Output:

array(2) {
  [0]=>
  string(2) "hi"
  [1]=>
  string(2) "oh"
}

What does + mean when used on arrays in PHP?

6
  • 6
    I notice your question had += and the accepted answer had +. From my testing they seem to behave the same. Commented Aug 24, 2012 at 17:24
  • 7
    The documentation sums it up pretty well Commented Aug 22, 2013 at 13:53
  • 1
    possible duplicate of Reference - What does this symbol mean in PHP? Commented Jun 26, 2014 at 3:40
  • @RobbieAverill - this is the question that that Reference question refers to. So if anything that Reference question is the duplicate Commented Nov 21, 2016 at 12:29
  • 3
    Is anyone else slightly horrified that such a fundamental thing as array concatenation has to be done through array_merge? It's as if arrays are by default associative arrays and numeric arrays are second class citizens. Commented Nov 21, 2016 at 12:35

9 Answers 9

322

Quoting from the PHP Manual on Language Operators

The + operator returns the right-hand array appended to the left-hand array; for keys that exist in both arrays, the elements from the left-hand array will be used, and the matching elements from the right-hand array will be ignored.

So if you do

$array1 = ['one',   'two',          'foo' => 'bar'];
$array2 = ['three', 'four', 'five', 'foo' => 'baz']; 

print_r($array1 + $array2);

You will get

Array
(
    [0] => one   // preserved from $array1 (left-hand array)
    [1] => two   // preserved from $array1 (left-hand array)
    [foo] => bar // preserved from $array1 (left-hand array)
    [2] => five  // added from $array2 (right-hand array)
)

So the logic of + is equivalent to the following snippet:

$union = $array1;

foreach ($array2 as $key => $value) {
    if (false === array_key_exists($key, $union)) {
        $union[$key] = $value;
    }
}

If you are interested in the details of the C-level implementation head to


Note, that + is different from how array_merge() would combine the arrays:

print_r(array_merge($array1, $array2));

would give you

Array
(
    [0] => one   // preserved from $array1
    [1] => two   // preserved from $array1
    [foo] => baz // overwritten from $array2
    [2] => three // appended from $array2
    [3] => four  // appended from $array2
    [4] => five  // appended from $array2
)

See linked pages for more examples.

Sign up to request clarification or add additional context in comments.

11 Comments

@Pacerier PHP produced by php.net doesn't have a formal spec, but both the + and array_merge call zend_hash_merge under the hood. This is also expected, since in PHP arrays are implemented as ordered hash maps.
@Pacerier The php.net online docs are the closest record to a specification, but IMO those docs fall way short of a true spec: one, they're updated after the code is written; two, they're not written to cover every special usage.
The behaviour of PHP's + and array_merge is perverse and unintuitive. They're the opposite way round from what a plain-English reading would intuitively tell you 'adding' or 'merging' arrays would do. Other languages/libraries use + to concatenate lists (e.g. in Python) and "merge" functions to add the key/value pairs from one object onto another (e.g. in lodash). Yet in PHP it's the other way round; array_merge can be used for concatenating list-like arrays but + cannot. Unlike array_merge, + always performs the operation that would be called a "merge" in any other language.
@icc97 they are indeed only HashMaps. See nikic.github.io/2014/12/22/…
I've tried submitting a bug to see if an alias array_concat could be created for array_merge.
|
26

The best example I found for using this is in a config array.

$user_vars = array("username"=>"John Doe");
$default_vars = array("username"=>"Unknown", "email"=>"[email protected]");

$config = $user_vars + $default_vars;

The $default_vars, as it suggests, is the array for default values. The $user_vars array will overwrite the values defined in $default_vars. Any missing values in $user_vars are now the defaults vars from $default_vars.

This would print_r as:

Array(2){
    "username" => "John Doe",
    "email" => "[email protected]"
}

I hope this helps!

Comments

8

From https://softonsofa.com/php-array_merge-vs-array_replace-vs-plus-aka-union/

That being said, we could think of the + operator as kinda redundant, since the same can be achieved with array_replace function.

However, there are cases, where it comes in handy: say you have an $options array being passed to a function/method, and there are also defaults to be used as fallback:

// we could do it like this
function foo(array $options)
{
   $defaults = ['foo' => 'bar'];

   $options = array_replace($defaults, $options);

   // ...
}

// but + here might be way better:
function foo(array $options)
{
   $options += ['foo' => 'bar'];

   // ...
}

Starting from PHP 7.4, array unpacking like [...$a, ...$b] can be used to replace array_merge($a, $b):

Advantages over array_merge

Spread operator should have a better performance than array_merge. It's because not only that spread operator is a language structure while array_merge is a function call, but also compile time optimization can be performant for constant arrays. array_merge only supports array, while spread operator also supports objects implementing Traversable.

that both contains no string keys:

String keys

In order to make the behavior consistent with argument unpacking, string keys are not supported. A recoverable error will be thrown once a string key is encountered.

Starting from PHP 8.1, array unpacking supports merging arrays with string key, so its behavior is same with array_merge()


From https://php.watch/versions/8.1/spread-operator-string-array-keys:

Array Unpacking vs array_merge

Array unpacking and array_merge results in identical values.

$array_unpack = [
    ...['a' => 'foo'],
    ...['b' => 'bar'],
    ...['c' => 'baz']
];
$array_merge = array_merge(
    ['a' => 'foo'],
    ['b' => 'bar'],
    ['c' => 'baz'],
);

$array_unpack === $array_merge; // true

Array Unpacking vs + operator

Array unpacking overwrites duplicate string keys, while the + operator ignores duplicate string keys.

var_dump(
     ['a' => 'foo']
    +['a' => 'bar']
);
// ['a' => 'foo'];

var_dump([
     ...['a' => 'foo'],
     ...['a' => 'bar']
]);
// ['a' => 'bar'];

Comments

6

This operator takes the union of two arrays (same as array_merge, except that with array_merge duplicate keys are overwritten).

The documentation for array operators is found here.

2 Comments

A word of caution for beginners here, the result of the operation is null if any one of the arrays is null. Some might not care about this assuming since it is a union operation, the result will be the proper (not-null) array if one of them is null. But, that holds true if one of the arrays is an empty array.
So, as a good practice, I think, we should initialize the input arrays as empty arrays. What do you guys say?
6

Carefull with numeric keys, if they should be preserved or if you don't want to loose anything

$a = array(2 => "a2", 4 => "a4", 5 => "a5");
$b = array(1 => "b1", 3 => "b3", 4 => "b4");

union

print_r($a+$b);
Array
(
    [2] => a2
    [4] => a4
    [5] => a5
    [1] => b1
    [3] => b3
)

merge

print_r(array_merge($a, $b));
Array
(
    [0] => a2
    [1] => a4
    [2] => a5
    [3] => b1
    [4] => b3
    [5] => b4
)

Comments

4

The + operator produces the same results as array_replace(). However since the operator arguments are reversed, the ordering of the resulting array may also be different.

Expanding on another example from this page:

$array1 = array('one', 'two', 'foo' => 'bar');
$array2 = array('three', 'four', 'five', 'foo' => 'baz'); 

print_r($array1 + $array2);
print_r(array_replace($array2, $array1)); //note reversed argument order

outputs:

Array
(
    [0] => one   // preserved from $array1
    [1] => two   // preserved from $array1
    [foo] => bar // preserved from $array1
    [2] => five  // added from $array2
)
Array
(
    [0] => one   // preserved from $array1
    [1] => two   // preserved from $array1
    [2] => five  // added from $array2
    [foo] => bar // preserved from $array1
)

1 Comment

Is the order of + guaranteed by the specs? What about array_replace?
1
  1. Array plus operation treats all array as assoc array.
  2. When key conflict during plus, left(previous) value will be kept

I post the code below to make things clear.

$a + $b = array_plus($a, $b)

function array_plus($a, $b){
    $results = array();
    foreach($a as $k=>$v) if(!isset($results[$k]))$results[$k] = $v;
    foreach($b as $k=>$v) if(!isset($results[$k]))$results[$k] = $v;
    return $results;
}

2 Comments

@Tamlyn's code run seems to prove that your claim "array plus operation treats all array as assoc array" is wrong.
@Pacerier that key could be integer as well.
-4

It will append the new array to the previous.

Comments

-6
$var1 = "example";
$var2 = "test";
$output = array_merge((array)$var1,(array)$var2);
print_r($output);

Array ( [0] => example [1] => test )

3 Comments

This thread mentions a few times how array_merge() is NOT congruent.
@doublejosh, "congruent"? Meaning?
They are not the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.