Choose array_merge or array union according to key ownership
A configuration override should replace a default timeout. An array union unexpectedly keeps the old value, while a numeric-key merge unexpectedly renumbers entries. These operators implement different rules rather than different spellings of the same merge.
Make precedence visible
<?php
$defaults = ['timeout' => 5, 'retries' => 1];
$overrides = ['timeout' => 12];
assert(array_merge($defaults, $overrides)['timeout'] === 12);
assert(($defaults + $overrides)['timeout'] === 5);
assert(($overrides + $defaults)['timeout'] === 12);
assert(array_merge([7 => 'a'], [9 => 'b']) === ['a', 'b']);
String-key conflicts in array_merge take the later value. Array union keeps the left-hand value for an existing key. Numeric keys in a merge are appended and renumbered. If those numeric keys are external IDs, renumbering loses information.
Nested configuration needs another decision
A top-level merge does not automatically merge every nested setting in the way your application expects. Decide whether an override replaces a whole nested object or only selected fields. Recursive helpers have their own behavior, particularly around repeated values; test the final structure instead of trusting the word recursive.
For application configuration, start with a small explicit fixture containing a conflict, an added key, a numeric key, and a nested object. Write the expected final array by hand. That fixture becomes a readable statement of precedence for later maintainers.
Do not use an unvalidated request array as a configuration override. Correct merge semantics still allow a caller to replace sensitive settings if you accept arbitrary keys.
Reference
Official documentation. The examples here illustrate the stated boundary; adapt them to your application and test its failure paths.