Merge Arrays
Merging combines two or more arrays into one. Typical tasks include joining lists, applying user settings on top of defaults, layering configuration files, and pairing keys with values. PHP offers several ways to merge, and they handle duplicate keys differently — choosing the wrong one is a common source of bugs.
| Technique | Integer Keys | Duplicate String Keys | Nested Arrays |
|---|---|---|---|
array_merge($a, $b) | Renumbered | Later value wins | Replaced as a whole |
[...$a, ...$b] | Renumbered | Later value wins (PHP 8.1+) | Replaced as a whole |
$a + $b | Kept | First value wins | Replaced as a whole |
array_replace($a, $b) | Kept | Later value wins | Replaced as a whole |
array_replace_recursive($a, $b) | Kept | Later value wins | Merged level by level |
array_merge_recursive($a, $b) | Renumbered | Values combined into arrays | Merged level by level |
array_combine($keys, $values) | — | — | Builds a new array from two lists |
array_merge()
Lists
<?php
$morning = ['Yoga', 'Breakfast'];
$evening = ['Walk', 'Dinner'];
$day = array_merge($morning, $evening);
print_r($day);
Output
Array
(
[0] => Yoga
[1] => Breakfast
[2] => Walk
[3] => Dinner
)
All elements are combined, and integer keys are renumbered from 0.
Associative Arrays
<?php
$defaults = ['theme' => 'light', 'language' => 'en', 'perPage' => 20];
$userSettings = ['language' => 'ta', 'perPage' => 50];
print_r(array_merge($defaults, $userSettings));
Output
Array
(
[theme] => light
[language] => ta
[perPage] => 50
)
When the same string key exists in both arrays, the value from the later array wins. This makes array_merge($defaults, $overrides) a natural way to apply settings.
Integer Keys Are Renumbered — Even Meaningful Ones
<?php
$productsById = [101 => 'Gel Pen', 205 => 'Notebook'];
$moreProducts = [310 => 'Desk Lamp'];
print_r(array_merge($productsById, $moreProducts));
Output
Array
(
[0] => Gel Pen
[1] => Notebook
[2] => Desk Lamp
)
The product IDs are lost. When integer keys carry meaning (IDs, years, codes), use + or array_replace() instead.
Merging Many Arrays at Once
<?php
$pages = [[1, 2, 3], [4, 5], [6]];
$allItems = array_merge(...$pages);
echo implode(', ', $allItems) . "\n";
var_dump(array_merge(...[]));
Output
1, 2, 3, 4, 5, 6
array(0) {
}
array_merge(...$pages) unpacks the list of arrays as separate arguments, merging them in one call. Since PHP 7.4, array_merge() with no arguments returns an empty array, so this is safe even when $pages is empty.
Performance tip: Calling
$result = array_merge($result, $page)inside a loop copies the growing result on every iteration, which becomes slow for large data. Collect the pieces in an array and merge once witharray_merge(...$pieces).
The Union Operator +
<?php
$defaults = ['theme' => 'light', 'language' => 'en', 'perPage' => 20];
$userSettings = ['language' => 'ta', 'perPage' => 50];
print_r($userSettings + $defaults);
$productsById = [101 => 'Gel Pen', 205 => 'Notebook'];
$moreProducts = [310 => 'Desk Lamp', 101 => 'Blue Pen'];
print_r($productsById + $moreProducts);
Output
Array
(
[language] => ta
[perPage] => 50
[theme] => light
)
Array
(
[101] => Gel Pen
[205] => Notebook
[310] => Desk Lamp
)
+keeps all keys from the left array and adds keys from the right array only if they are missing.- The left value wins for duplicates, so
$overrides + $defaultsfills in missing settings. - Integer keys are preserved: product
101keeps the name from the left array, and310is added.
Remember that + is not a way to append lists: ['A', 'B'] + ['C', 'D', 'E'] gives ['A', 'B', 'E'].
array_replace() and array_replace_recursive()
array_replace() works like array_merge() for string keys, but preserves integer keys:
<?php
$stockById = [101 => 50, 205 => 12];
$stockUpdate = [205 => 30, 310 => 8];
print_r(array_replace($stockById, $stockUpdate));
Output
Array
(
[101] => 50
[205] => 30
[310] => 8
)
Product 205 is updated, 310 is added, and all IDs remain intact.
Merging Nested Configuration
With nested arrays, array_replace() (like array_merge()) replaces each top-level entry as a whole. array_replace_recursive() merges level by level instead:
<?php
$defaults = ['db' => ['host' => 'localhost', 'port' => 3306]];
$override = ['db' => ['host' => 'db.prod']];
echo json_encode(array_replace($defaults, $override)) . "\n";
echo json_encode(array_replace_recursive($defaults, $override)) . "\n";
Output
{"db":{"host":"db.prod"}}
{"db":{"host":"db.prod","port":3306}}
array_replace()replaced the entiredbsection, losing the port.array_replace_recursive()replaced onlydb.hostand keptdb.port.
The array_merge_recursive() Trap
Despite its name, array_merge_recursive() is usually not what you want for configuration. When both arrays contain the same string key, it combines the values into an array instead of replacing:
<?php
$defaults = ['db' => ['host' => 'localhost', 'port' => 3306]];
$override = ['db' => ['host' => 'db.prod']];
echo json_encode(array_merge_recursive($defaults, $override)) . "\n";
Output
{"db":{"host":["localhost","db.prod"],"port":3306}}
The host is now an array of two values, which breaks any code expecting a string. Use array_replace_recursive() for "override" semantics. array_merge_recursive() fits only when you really want to collect values — for example, combining lists of tags from several sources under the same key.
array_combine() — Keys From One Array, Values From Another
<?php
$headers = ['sku', 'name', 'price'];
$row = ['PEN-01', 'Gel Pen', '20'];
print_r(array_combine($headers, $row));
Output
Array
(
[sku] => PEN-01
[name] => Gel Pen
[price] => 20
)
This is the standard way to turn a CSV row into an associative record using the header line. Both arrays must contain the same number of elements; otherwise PHP throws a ValueError.
Choosing the Right Tool
| Goal | Use |
|---|---|
| Join lists | array_merge() or [...$a, ...$b] |
| Apply overrides to settings (flat) | array_merge($defaults, $overrides) or array_replace() |
| Fill in missing defaults (flat) | $values + $defaults |
| Keep integer keys (IDs) | + or array_replace() |
| Override nested configuration | array_replace_recursive() |
| Collect values under shared keys | array_merge_recursive() |
| Pair keys with values | array_combine() |
Practical Example: Layered Configuration
Applications often combine configuration from several layers: built-in defaults, environment-specific settings, and local developer overrides.
<?php
$defaults = [
'app' => ['name' => 'GoCourse', 'debug' => false],
'db' => ['host' => 'localhost', 'port' => 3306, 'name' => 'gocourse'],
'mail' => ['driver' => 'smtp'],
];
$production = [
'db' => ['host' => 'db.internal'],
'mail' => ['driver' => 'ses'],
];
$local = [
'app' => ['debug' => true],
];
$config = array_replace_recursive($defaults, $production, $local);
echo json_encode($config, JSON_PRETTY_PRINT) . "\n";
Output
{
"app": {
"name": "GoCourse",
"debug": true
},
"db": {
"host": "db.internal",
"port": 3306,
"name": "gocourse"
},
"mail": {
"driver": "ses"
}
}
Step-by-Step Explanation
array_replace_recursive()accepts several arrays and applies them from left to right; later layers override earlier ones.- Only the keys present in each layer change: production overrides the database host and mail driver, and the local layer enables debugging.
- Every value that no layer overrides (app name, database port and name) comes from the defaults.
With array_merge(), the db section from $production would have replaced the whole default db section, losing the port and database name.
Common Mistakes
Using array_merge() on ID-Keyed Arrays
Effect: IDs are replaced with 0, 1, 2, ….
Fix: Use array_replace() or +.
Using + to Append Lists
Effect: Elements with existing keys are silently dropped.
Fix: Use array_merge() or the spread operator.
Using array_merge_recursive() for Overrides
Effect: Overridden values become arrays of all values.
Fix: Use array_replace_recursive().
Getting the Order Wrong
Effect: array_merge($overrides, $defaults) lets defaults overwrite the user's choices.
Fix: For array_merge()/array_replace(), put the overrides last; for +, put them first.
Merging Inside Loops
Effect: Slow code for large data.
Fix: Collect arrays, then call array_merge(...$arrays) once.
Best Practices
- Decide how duplicate keys should behave, then choose the function accordingly.
- Use
array_replace_recursive()for nested configuration layers. - Preserve meaningful integer keys with
array_replace()or+. - Merge many arrays in a single
array_merge(...$arrays)call. - Validate lengths before
array_combine().
Official reference: https://www.php.net/manual/en/function.array-merge.php