Generating patches

JsonPatch::diff() returns operations that transform one value into another.

<?php
use Alto\JsonPatch\JsonPatch;

$before = ['version' => 1, 'status' => 'draft'];
$after = ['version' => 2, 'status' => 'published', 'author' => 'Alice'];

$patch = JsonPatch::diff($before, $after);
$result = JsonPatch::apply($before, $patch);

assert($after === $result);

Object keys are removed, added, or recursively changed. Scalar values and changes between object and list shapes produce replace operations.

Lists

Lists use a longest common subsequence by default. This produces stable remove and add operations while preserving unchanged elements.

<?php
use Alto\JsonPatch\DiffOptions;

$patch = JsonPatch::diff(
    ['tags' => ['php', 'json']],
    ['tags' => ['php', 'api']],
    new DiffOptions(useLcs: true),
);

Set useLcs to false to replace a changed list as one value.

Lists with identities

Identity-based diffing can express moves and nested item changes:

<?php
$before = ['items' => [
    ['id' => 'a', 'quantity' => 1],
    ['id' => 'b', 'quantity' => 2],
]];

$after = ['items' => [
    ['id' => 'b', 'quantity' => 3],
    ['id' => 'c', 'quantity' => 1],
]];

$options = new DiffOptions(
    listIdentityByPointer: ['/items' => 'id'],
);

$patch = JsonPatch::diff($before, $after, $options);

Every item in both lists must be an object-like associative array with a unique string or integer identity. Otherwise, diffing falls back to the normal list strategy.

Diffing stops at a nesting depth of 512 and raises JsonPatchException rather than recursing without a bound.