array_merge_recursive
Merges arrays recursively, combining values that share a string key into a nested array.
array_merge_recursive(array ...$arrays): array- Added in PHP 4.0.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$arraysoptional | array | Any number of arrays to merge recursively, processed left to right. |
Return value
arrayA new array in which same-string-key values have been merged recursively. Returns an empty array when called with no arguments.
Errors
Throws a TypeError if any argument is not an array.
Changelog
- PHP 7.4.0Can now be called with no arguments at all; previously at least one array was required.
array_merge_recursive() works like array_merge() until it hits a string key that exists in more than one array. At that point it does not overwrite — it combines both values into an array and keeps descending.
What that actually means
If both values are arrays, they are merged recursively, which is usually what you wanted. If either value is a scalar, the two scalars are collected into a new array. So merging ['name' => 'Ada'] with ['name' => 'Bo'] gives ['name' => ['Ada', 'Bo']], not ['name' => 'Bo']. That single behaviour is why this function is the wrong choice for merging configuration files, and why array_replace_recursive() exists.
When to reach for it
Reach for it when accumulating is genuinely what you want: collecting validation messages per field from several validators, gathering tags per category from multiple sources, building a multi-value map where duplicates should all be kept. In those cases the automatic promotion of scalars to arrays is the feature, not the bug.
Integer keys behave exactly as in array_merge(): values are appended and the keys are renumbered from 0, at every level of nesting. That means a nested list is concatenated rather than overwritten, which can grow arrays unexpectedly if you call the function repeatedly against the same defaults.
Example
When to use it
- Collect validation errors per field from several independent validators
- Gather tags or permissions for the same key from multiple sources
- Build a grouped map where every duplicate value must be kept
- Combine partial nested fixtures in tests where duplicates should accumulate
Frequently asked questions
What is the difference between array_merge_recursive and array_replace_recursive?
array_merge_recursive combines duplicate string-key values into an array and appends integer-keyed values; array_replace_recursive overwrites them. For applying overrides on top of defaults you almost always want array_replace_recursive.
Why did my scalar value become an array?
Because two of the merged arrays defined the same string key with a scalar value, and array_merge_recursive collects both rather than picking one. That promotion to an array is the documented behaviour, not a bug.
Does array_merge_recursive overwrite numeric keys?
No — at every level of nesting, integer-keyed values are appended and the keys are renumbered from 0. Nested lists are concatenated, never replaced.
More examples
Collecting errors per field
Both sources contribute to the email key and both messages survive, giving a two-element list under 'email'. This accumulating behaviour is exactly what you want for error bags, unlike config merging.
<?php
$lengthErrors = ['email' => ['too short']];
$formatErrors = ['email' => ['not a valid address'], 'age' => ['must be a number']];
$all = array_merge_recursive($lengthErrors, $formatErrors);
print_r($all);
echo count($all['email']), " email problems\n";Why it is wrong for config overrides
array_merge_recursive turns port into [3306, 3307] because both arrays define it. array_replace_recursive gives the intended result, port => 3307, with host untouched. Compare the two outputs side by side before choosing.
<?php
$defaults = ['db' => ['host' => 'localhost', 'port' => 3306]];
$override = ['db' => ['port' => 3307]];
print_r(array_merge_recursive($defaults, $override));
print_r(array_replace_recursive($defaults, $override));Integer keys are always appended
Even though both use key 0, nothing is overwritten: the nested list holds 'first' and 'second' renumbered as 0 and 1. Repeatedly merging the same defaults this way makes nested lists grow on every call.
<?php
$a = ['list' => [0 => 'first']];
$b = ['list' => [0 => 'second']];
$r = array_merge_recursive($a, $b);
print_r($r);
echo "list has ", count($r['list']), " items\n";
Discussion