Skip to content
← Back to Snippets
Code

Nested Array Diff Report

Creates a dot-path diff report for changed, added, and removed values inside nested arrays.

Purpose

Creates a dot-path diff report for changed, added, and removed values inside nested arrays.

Snippet details

ContextArrayLevelAdvancedCopy-and-paste statusMarked safe after review.

Categories

  • Security

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Nested Array Diff Report.
 *
 * Purpose:
 * Compares two nested arrays and reports added, removed, and changed values
 * using dot-path keys that are easy to display in an audit screen.
 *
 * @param array $before Previous nested array state.
 * @param array $after New nested array state.
 * @param string $prefix Internal dot-path prefix.
 * @return array Nested array diff report.
 */
function ogSnippetNestedArrayDiffReport(array $before, array $after, string $prefix = ''): array {
	$changes = array();
	$keys = array();

	foreach ($before as $key => $value) {
		$keys[$key] = $key;
	}

	foreach ($after as $key => $value) {
		$keys[$key] = $key;
	}

	foreach ($keys as $key) {
		$path = (string) $key;

		if ($prefix !== '') {
			$path = $prefix.'.'.$path;
		}

		$before_exists = array_key_exists($key, $before);
		$after_exists = array_key_exists($key, $after);

		if ($before_exists === false) {
			$changes[] = array('path' => $path, 'type' => 'added', 'before' => null, 'after' => $after[$key]);
			continue;
		}

		if ($after_exists === false) {
			$changes[] = array('path' => $path, 'type' => 'removed', 'before' => $before[$key], 'after' => null);
			continue;
		}

		if (is_array($before[$key]) === true && is_array($after[$key]) === true) {
			$nested_changes = ogSnippetNestedArrayDiffReport($before[$key], $after[$key], $path);

			foreach ($nested_changes as $nested_change) {
				$changes[] = $nested_change;
			}

			continue;
		}

		if ($before[$key] !== $after[$key]) {
			$changes[] = array('path' => $path, 'type' => 'changed', 'before' => $before[$key], 'after' => $after[$key]);
		}
	}

	return $changes;
}

$before = array('ship' => array('name' => 'Rocinante', 'status' => 'docked'));
$after = array('ship' => array('name' => 'Rocinante', 'status' => 'burning'));
$diff_report = ogSnippetNestedArrayDiffReport($before, $after);

echo 'The Expanse nested array diff count: '.count($diff_report);