diff --git a/.gitignore b/.gitignore index 45c7b33..9ce6d76 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /json-diff /json-diff.tar.gz /composer.lock -/composer.phar \ No newline at end of file +/composer.phar +/clover.xml \ No newline at end of file diff --git a/src/JsonDiff.php b/src/JsonDiff.php index 1c4e9e3..5295f60 100644 --- a/src/JsonDiff.php +++ b/src/JsonDiff.php @@ -231,6 +231,8 @@ private function process($original, $new) $newOrdered = array(); $originalKeys = $original instanceof \stdClass ? get_object_vars($original) : $original; + $isArray = is_array($original); + $removedOffset = 0; foreach ($originalKeys as $key => $originalValue) { if ($this->options & self::STOP_ON_DIFF) { @@ -241,8 +243,12 @@ private function process($original, $new) $path = $this->path; $pathItems = $this->pathItems; - $this->path .= '/' . JsonPointer::escapeSegment($key, $this->options & self::JSON_URI_FRAGMENT_ID); - $this->pathItems[] = $key; + $actualKey = $key; + if ($isArray) { + $actualKey -= $removedOffset; + } + $this->path .= '/' . JsonPointer::escapeSegment($actualKey, $this->options & self::JSON_URI_FRAGMENT_ID); + $this->pathItems[] = $actualKey; if (array_key_exists($key, $newArray)) { $newOrdered[$key] = $this->process($originalValue, $newArray[$key]); @@ -253,6 +259,9 @@ private function process($original, $new) return null; } $this->removedPaths [] = $this->path; + if ($isArray) { + $removedOffset++; + } $this->jsonPatch->op(new Remove($this->path)); diff --git a/src/JsonPointer.php b/src/JsonPointer.php index 6f7960f..55e9e2d 100644 --- a/src/JsonPointer.php +++ b/src/JsonPointer.php @@ -202,7 +202,9 @@ public static function remove(&$holder, $pathItems) unset($parent->$refKey); } else { unset($parent[$refKey]); - $parent = array_values($parent); + if ($refKey !== count($parent)) { + $parent = array_values($parent); + } } } return $ref; diff --git a/tests/src/Issues/Issue6Test.php b/tests/src/Issues/Issue6Test.php new file mode 100644 index 0000000..36292ed --- /dev/null +++ b/tests/src/Issues/Issue6Test.php @@ -0,0 +1,90 @@ +getPatch(); + + $this->assertSame(<<<'JSON' +[ + { + "value": "a", + "op": "test", + "path": "/0/name" + }, + { + "value": "b", + "op": "replace", + "path": "/0/name" + }, + { + "op": "remove", + "path": "/1" + }, + { + "op": "remove", + "path": "/1" + } +] +JSON + , json_encode($patch, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES)); + + $json1a = $json1; + $patch->apply($json1a); + + $this->assertEquals($json2, $json1a); + } + + public function testOriginal() + { + $originalJson = '[{"name":"a"},{"name":"b"},{"name":"c"}]'; + $newJson = '[{"name":"b"}]'; + $diff = new JsonDiff(json_decode($originalJson), json_decode($newJson)); + + $patchJson = json_decode(json_encode($diff->getPatch()->jsonSerialize()), true); + + $original = json_decode($originalJson); + $patch = JsonPatch::import($patchJson); + $patch->apply($original); + $this->assertEquals($original, json_decode($newJson)); + } + + + public function testDoubleInverseRemove() + { + $json1 = json_decode('[{"name":"a"},{"name":"b"},{"name":"c"}]'); + $json2 = json_decode('[{"name":"b"}]'); + + $patch = JsonPatch::import(json_decode('[{"op":"remove","path":"/2"},{"op":"remove","path":"/0"}]')); + + $json1a = $json1; + $patch->apply($json1a); + $this->assertEquals(json_encode($json2), json_encode($json1a)); + } + + public function testDoubleRemove() + { + $json1 = json_decode('[{"name":"a"},{"name":"b"},{"name":"c"}]'); + $json2 = json_decode('[{"name":"b"}]'); + + $patch = JsonPatch::import(json_decode('[{"op":"remove","path":"/0"},{"op":"remove","path":"/1"}]')); + + $json1a = $json1; + $patch->apply($json1a); + $this->assertEquals(json_encode($json2), json_encode($json1a)); + } +} \ No newline at end of file