Deep Array Diff: Find All Changes Between Two Arrays
PHP Code Editor
Execution Result
Ready to execute
Click the "Run Script" button to see the output here
Description
Detecting what changed between two versions of a nested array is useful for auditing config changes, tracking state mutations, or generating database changelogs. PHP has array_diff and array_diff_assoc for flat arrays, but neither recurses into nested structures.
This function merges keys from both arrays and checks each one. A key present only in $new is marked added. Present only in $old means removed. When both values are arrays, the function recurses with the current key appended to the path using dot notation. Any other value difference is marked changed. The result is a flat map where each entry is a dot-notation path (like database.host) pointing to a descriptor with the change type and the old and new values.
<?php
/**
* Deep diff between two arrays.
* Returns changes as ['path' => ['old' => ..., 'new' => ...]]
* Paths use dot notation: 'user.address.city'
*/
function arrayDiff(array $old, array $new, string $prefix = ''): array
{
$changes = [];
$allKeys = array_unique(array_merge(array_keys($old), array_keys($new)));
foreach ($allKeys as $key) {
$path = $prefix !== '' ? "$prefix.$key" : (string)$key;
$inOld = array_key_exists($key, $old);
$inNew = array_key_exists($key, $new);
if (!$inOld) {
$changes[$path] = ['type' => 'added', 'old' => null, 'new' => $new[$key]];
} elseif (!$inNew) {
$changes[$path] = ['type' => 'removed', 'old' => $old[$key], 'new' => null];
} elseif (is_array($old[$key]) && is_array($new[$key])) {
$changes = array_merge($changes, arrayDiff($old[$key], $new[$key], $path));
} elseif ($old[$key] !== $new[$key]) {
$changes[$path] = ['type' => 'changed', 'old' => $old[$key], 'new' => $new[$key]];
}
}
return $changes;
}
// Demo: config change detection
$oldConfig = [
'app' => ['name' => 'MyApp', 'debug' => false, 'version' => '1.0'],
'database' => ['host' => 'localhost', 'port' => 3306, 'name' => 'mydb'],
'cache' => ['driver' => 'file', 'ttl' => 3600],
'removed' => 'this key will be gone',
];
$newConfig = [
'app' => ['name' => 'MyApp', 'debug' => true, 'version' => '2.0'],
'database' => ['host' => 'db.prod.com', 'port' => 3306, 'name' => 'mydb'],
'cache' => ['driver' => 'redis', 'ttl' => 3600, 'prefix' => 'app:'],
'added' => 'this key is new',
];
$diff = arrayDiff($oldConfig, $newConfig);
foreach ($diff as $path => $change) {
$type = strtoupper($change['type']);
printf("[%s] %s\n", $type, $path);
if ($change['old'] !== null) printf(" old: %s\n", json_encode($change['old']));
if ($change['new'] !== null) printf(" new: %s\n", json_encode($change['new']));
}
Comments
No comments yet
Be the first to share your thoughts!