rikudou/array-merge-recursive
A lightweight PHP utility for recursively merging arrays with predictable results. It combines nested structures while letting later values take precedence, making it useful for configs, defaults, and deep data composition. Framework-agnostic and easy to use.
Install:
composer require rikudou/array-merge-recursive
Read src/ArrayMergeRecursive.php first. This is a one-class package — the entire behaviour lives in a single recursive method, so 60 seconds of reading gives you the full contract. Skim the composer.json for the PHP constraint and the (only) release tag (2021-04-22, so expect no forward-compatibility fixes).
First use case — the one it was written for: replace PHP's built-in array_merge_recursive in a config loader, where duplicate string keys must overwrite instead of being turned into a nested array.
// The problem with the built-in:
array_merge_recursive(['db' => ['host' => 'localhost']], ['db' => ['host' => 'prod']]);
// ['db' => ['host' => ['localhost', 'prod']]] <-- not what anyone wants
// What this package gives you:
ArrayMergeRecursive::merge(['db' => ['host' => 'localhost']], ['db' => ['host' => 'prod']]);
// ['db' => ['host' => 'prod']]
Confirm the facade name/signature in src/ on day one (it mirrors array_merge_recursive's variadic shape: one or more arrays in, one array out).
Config overlay (the 90% case). Layer defaults → environment → user overrides in order; later arguments win at the leaf:
$config = ArrayMergeRecursive::merge(
require base_path('config/package.php'), // package defaults
require config_path('package.php'), // published overrides
);
Laravel service provider / config repository. Because the package has no ServiceProvider and no auto-discovery, wire it manually wherever you currently call array_merge_recursive:
public function register()
{
$this->mergeConfigFrom(base_path('config/defaults.php'), 'package');
// replace the shallow merge with:
config(['package' => ArrayMergeRecursive::merge(
config('package'),
require base_path('config/shared.php'),
)]);
}
Merging JSON-decoded payloads before validation. When json_decode(..., true) gives you assoc arrays that share string keys but differ in nesting, this preserves structure instead of producing [old, new] pairs that then fail required|string validation unexpectedly.
DTO / SDK option resolution. Same pattern for building an options array (['retry' => ['attempts' => 2]]) from a base and a caller-supplied array — you want deep merge but scalar replacement, which is exactly the difference between this and array_merge_recursive.
Workflow rule of thumb. Use it wherever you catch yourself writing a recursive foreach that does $a[$k] = is_array($v) && isset($a[$k]) && is_array($a[$k]) ? merge($a[$k], $v) : $v;. Reach for the package instead; it's the same algorithm without the reimplementation drift.
array_replace_recursive first. PHP's built-in already replaces scalar string keys and recurses into arrays. The real difference shows up on numeric/list keys and on scalar↔array collisions — test those two cases explicitly against your expected output before committing to the dependency. If array_replace_recursive matches your needs, drop the dependency entirely.merge($base, $override) means $override wins. Reverse the arguments and you get the opposite; there's no "priority" flag, only argument order.'x' and the other ['y' => 1], only one type can survive. Verify which wins in src/; the built-ins disagree with each other here and this package may follow either.null handling, no type juggling. It's array-in, array-out; passing null or an ArrayAccess object is your problem, not the package's. Guard at the call site.composer.lock, and if you're on a modern PHP with declare(strict_types=1) across the codebase, run the test suite once to confirm the untyped signature accepts your arguments.var_dump the inputs side by side first — the failure is almost always a numeric-key collision or a scalar/array mismatch, not the recursion itself. Then dump the merged result and diff key paths.How can I help you explore Laravel packages today?