Skip to content

Commit 9954d61

Browse files
committed
adding JSON Patch support
1 parent 7ad944d commit 9954d61

32 files changed

+1213
-284
lines changed

Diff for: .travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ language: php
22
php:
33
- nightly
44
- hhvm
5+
- 7.2
56
- 7.1
67
- 7.0
78
- 5.6

Diff for: README.md

+128-85
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ A PHP implementation for finding unordered diff between two `JSON` documents.
1212
* To simplify changes review between two `JSON` files you can use a standard `diff` tool on rearranged pretty-printed `JSON`.
1313
* To detect breaking changes by analyzing removals and changes from original `JSON`.
1414
* To keep original order of object sets (for example `swagger.json` [parameters](https://swagger.io/docs/specification/describing-parameters/) list).
15+
* To make and apply JSON Patches, specified in [RFC 6902](http://tools.ietf.org/html/rfc6902) from the IETF.
1516

1617
## Installation
1718

@@ -52,6 +53,9 @@ $r = new JsonDiff(
5253

5354
On created object you have several handy methods.
5455

56+
### `getPatch`
57+
Returns JsonPatch of difference
58+
5559
### `getRearranged`
5660
Returns new value, rearranged with original order.
5761

@@ -98,7 +102,7 @@ $originalJson = <<<'JSON'
98102
"sub2": "b"
99103
},
100104
"key4": [
101-
{"a":1, "b":true}, {"a":2, "b":false}, {"a":3}
105+
{"a":1, "b":true, "subs": [{"s":1}, {"s":2}, {"s":3}]}, {"a":2, "b":false}, {"a":3}
102106
]
103107
}
104108
JSON;
@@ -108,7 +112,7 @@ $newJson = <<<'JSON'
108112
"key5": "wat",
109113
"key1": [5, 1, 2, 3],
110114
"key4": [
111-
{"c":false, "a":2}, {"a":1, "b":true}, {"c":1, "a":3}
115+
{"c":false, "a":2}, {"a":1, "b":true, "subs": [{"s":3, "add": true}, {"s":2}, {"s":1}]}, {"c":1, "a":3}
112116
],
113117
"key3": {
114118
"sub3": 0,
@@ -118,85 +122,125 @@ $newJson = <<<'JSON'
118122
}
119123
JSON;
120124

121-
$expected = <<<'JSON'
122-
{
123-
"key1": [5, 1, 2, 3],
124-
"key3": {
125-
"sub1": "c",
126-
"sub2": false,
127-
"sub3": 0
128-
},
129-
"key4": [
130-
{"a":1, "b":true}, {"a":2, "c":false}, {"a":3, "c":1}
131-
],
132-
"key5": "wat"
133-
}
125+
$patchJson = <<<'JSON'
126+
[
127+
{"value":4,"op":"test","path":"/key1/0"},
128+
{"value":5,"op":"replace","path":"/key1/0"},
129+
130+
{"op":"remove","path":"/key2"},
131+
132+
{"op":"remove","path":"/key3/sub0"},
133+
134+
{"value":"a","op":"test","path":"/key3/sub1"},
135+
{"value":"c","op":"replace","path":"/key3/sub1"},
136+
137+
{"value":"b","op":"test","path":"/key3/sub2"},
138+
{"value":false,"op":"replace","path":"/key3/sub2"},
139+
140+
{"value":0,"op":"add","path":"/key3/sub3"},
141+
142+
{"value":true,"op":"add","path":"/key4/0/subs/2/add"},
143+
144+
{"op":"remove","path":"/key4/1/b"},
145+
146+
{"value":false,"op":"add","path":"/key4/1/c"},
147+
148+
{"value":1,"op":"add","path":"/key4/2/c"},
149+
150+
{"value":"wat","op":"add","path":"/key5"}
151+
]
134152
JSON;
135153

136-
$r = new JsonDiff(json_decode($originalJson), json_decode($newJson));
137-
$this->assertSame(
138-
json_encode(json_decode($expected), JSON_PRETTY_PRINT),
139-
json_encode($r->getRearranged(), JSON_PRETTY_PRINT)
140-
);
141-
$this->assertSame('{"key3":{"sub3":0},"key4":{"1":{"c":false},"2":{"c":1}},"key5":"wat"}',
142-
json_encode($r->getAdded()));
143-
$this->assertSame(array(
144-
'#/key3/sub3',
145-
'#/key4/1/c',
146-
'#/key4/2/c',
147-
'#/key5',
148-
), $r->getAddedPaths());
149-
$this->assertSame('{"key2":2,"key3":{"sub0":0},"key4":{"1":{"b":false}}}',
150-
json_encode($r->getRemoved()));
151-
$this->assertSame(array(
152-
'#/key2',
153-
'#/key3/sub0',
154-
'#/key4/1/b',
155-
), $r->getRemovedPaths());
156-
157-
$this->assertSame(array(
158-
'#/key1/0',
159-
'#/key3/sub1',
160-
'#/key3/sub2',
161-
), $r->getModifiedPaths());
162-
163-
$this->assertSame('{"key1":[4],"key3":{"sub1":"a","sub2":"b"}}', json_encode($r->getModifiedOriginal()));
164-
$this->assertSame('{"key1":[5],"key3":{"sub1":"c","sub2":false}}', json_encode($r->getModifiedNew()));
154+
$diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), JsonDiff::REARRANGE_ARRAYS);
155+
$this->assertEquals(json_decode($patchJson), $diff->getPatch()->jsonSerialize());
156+
157+
$original = json_decode($originalJson);
158+
$patch = JsonPatch::import(json_decode($patchJson));
159+
$patch->apply($original);
160+
$this->assertEquals($diff->getRearranged(), $original);
165161
```
166162

167163
## CLI tool
168164

169165
### Usage
170166

171167
```
172-
json-diff --help
173-
v1.0.0 json-diff
174-
JSON diff and rearrange tool for PHP, https://github.com/swaggest/json-diff
168+
bin/json-diff --help
169+
JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff
170+
Usage:
171+
json-diff <action>
172+
action Action name
173+
Allowed values: diff, apply, rearrange, info
174+
```
175+
176+
```
177+
bin/json-diff diff --help
178+
v2.0.0 json-diff diff
179+
JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff
180+
Make patch from two json documents, output to STDOUT
175181
Usage:
176-
json-diff <action> <originalPath> <newPath>
177-
action Action to perform
178-
Allowed values: rearrange, changes, removals, additions, modifications
179-
originalPath Path to old (original) json file
180-
newPath Path to new json file
182+
json-diff diff <originalPath> <newPath>
183+
originalPath Path to old (original) json file
184+
newPath Path to new json file
181185
182186
Options:
183-
--out <out> Path to output result json file, STDOUT if not specified
184-
--show-paths Show JSON paths
185-
--show-json Show JSON result
187+
--pretty Pretty-print result JSON
188+
--rearrange-arrays Rearrange arrays to match original
189+
```
190+
191+
```
192+
bin/json-diff apply --help
193+
v2.0.0 json-diff apply
194+
JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff
195+
Apply patch to base json document, output to STDOUT
196+
Usage:
197+
json-diff apply [patchPath] [basePath]
198+
patchPath Path to JSON patch file
199+
basePath Path to JSON base file
186200
187-
Misc:
188-
--help Show usage information
189-
--version Show version
190-
--bash-completion Generate bash completion
191-
--install Install to /usr/local/bin/
201+
Options:
202+
--pretty Pretty-print result JSON
203+
--rearrange-arrays Rearrange arrays to match original
204+
```
205+
206+
```
207+
bin/json-diff rearrange --help
208+
v2.0.0 json-diff rearrange
209+
JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff
210+
Rearrange json document in the order of another (original) json document
211+
Usage:
212+
json-diff rearrange <originalPath> <newPath>
213+
originalPath Path to old (original) json file
214+
newPath Path to new json file
215+
216+
Options:
217+
--pretty Pretty-print result JSON
218+
--rearrange-arrays Rearrange arrays to match original
219+
```
220+
221+
```
222+
bin/json-diff info --help
223+
v2.0.0 json-diff info
224+
JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff
225+
Show diff info for two JSON documents
226+
Usage:
227+
json-diff info <originalPath> <newPath>
228+
originalPath Path to old (original) json file
229+
newPath Path to new json file
230+
231+
Options:
232+
--pretty Pretty-print result JSON
233+
--rearrange-arrays Rearrange arrays to match original
234+
--with-contents Add content to output
235+
--with-paths Add paths to output
192236
```
193237

194238
### Examples
195239

196240
Using with standard `diff`
197241

198242
```
199-
json-diff rearrange ./composer.json ./composer2.json --show-json | diff ./composer.json -
243+
json-diff rearrange ./composer.json ./composer2.json | diff ./composer.json -
200244
3c3
201245
< "description": "JSON diff and merge tool for PHP",
202246
---
@@ -221,30 +265,29 @@ json-diff rearrange ./composer.json ./composer2.json --show-json | diff ./compos
221265
Showing differences in `JSON` mode
222266

223267
```
224-
bin/json-diff changes ./composer.json ./composer2.json --show-json --show-paths
225-
#/license
226-
#/authors
227-
#/bin
228-
#/description
268+
bin/json-diff info tests/assets/original.json tests/assets/new.json --with-paths --pretty
229269
{
230-
"removals": {
231-
"license": "MIT",
232-
"authors": [
233-
{
234-
"name": "Viacheslav Poturaev",
235-
"email": "[email protected]"
236-
}
237-
],
238-
"bin": [
239-
"bin/json-diff"
240-
]
241-
},
242-
"additions": null,
243-
"modifiedOriginal": {
244-
"description": "JSON diff and merge tool for PHP"
245-
},
246-
"modifiedNew": {
247-
"description": "JSON diff and merge tool for PHPH"
248-
}
270+
"addedCnt": 4,
271+
"modifiedCnt": 4,
272+
"removedCnt": 3,
273+
"addedPaths": [
274+
"/key3/sub3",
275+
"/key4/0/c",
276+
"/key4/2/c",
277+
"/key5"
278+
],
279+
"modifiedPaths": [
280+
"/key1/0",
281+
"/key3/sub1",
282+
"/key3/sub2",
283+
"/key4/0/a",
284+
"/key4/1/a",
285+
"/key4/1/b"
286+
],
287+
"removedPaths": [
288+
"/key2",
289+
"/key3/sub0",
290+
"/key4/0/b"
291+
]
249292
}
250293
```

Diff for: bin/json-diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ foreach (array(__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoloa
99
}
1010
}
1111

12-
\Yaoi\Cli\Command\Runner::create(new \Swaggest\JsonDiff\Cli\Diff())->run();
12+
\Yaoi\Cli\Command\Application\Runner::create(new \Swaggest\JsonDiff\Cli\App())->run();

Diff for: src/Cli/App.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Swaggest\JsonDiff\Cli;
4+
5+
use Yaoi\Command;
6+
use Yaoi\Command\Definition;
7+
8+
class App extends Command\Application
9+
{
10+
public $diff;
11+
public $apply;
12+
public $rearrange;
13+
public $info;
14+
15+
/**
16+
* @param Definition $definition
17+
* @param \stdClass|static $commandDefinitions
18+
*/
19+
static function setUpCommands(Definition $definition, $commandDefinitions)
20+
{
21+
$definition->name = 'json-diff';
22+
$definition->version = 'v2.0.0';
23+
$definition->description = 'JSON diff and apply tool for PHP, https://github.com/swaggest/json-diff';
24+
25+
$commandDefinitions->diff = Diff::definition();
26+
$commandDefinitions->apply = Apply::definition();
27+
$commandDefinitions->rearrange = Rearrange::definition();
28+
$commandDefinitions->info = Info::definition();
29+
}
30+
}

Diff for: src/Cli/Apply.php

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Swaggest\JsonDiff\Cli;
4+
5+
use Swaggest\JsonDiff\Exception;
6+
use Swaggest\JsonDiff\JsonPatch;
7+
use Yaoi\Command;
8+
use Yaoi\Command\Definition;
9+
10+
class Apply extends Base
11+
{
12+
public $patchPath;
13+
public $basePath;
14+
public $isURIFragmentId = false;
15+
16+
/**
17+
* @param Definition $definition
18+
* @param \stdClass|static $options
19+
*/
20+
static function setUpDefinition(Definition $definition, $options)
21+
{
22+
$options->patchPath = Command\Option::create()->setType()->setIsUnnamed()
23+
->setDescription('Path to JSON patch file');
24+
$options->basePath = Command\Option::create()->setType()->setIsUnnamed()
25+
->setDescription('Path to JSON base file');
26+
$options->pretty = Command\Option::create()
27+
->setDescription('Pretty-print result JSON');
28+
$options->rearrangeArrays = Command\Option::create()
29+
->setDescription('Rearrange arrays to match original');
30+
$definition->description = 'Apply patch to base json document, output to STDOUT';
31+
32+
}
33+
34+
public function performAction()
35+
{
36+
$patchJson = file_get_contents($this->patchPath);
37+
if (!$patchJson) {
38+
$this->response->error('Unable to read ' . $this->patchPath);
39+
return;
40+
}
41+
42+
$baseJson = file_get_contents($this->basePath);
43+
if (!$baseJson) {
44+
$this->response->error('Unable to read ' . $this->basePath);
45+
return;
46+
}
47+
48+
try {
49+
$patch = JsonPatch::import(json_decode($patchJson));
50+
$base = json_decode($baseJson);
51+
$patch->apply($base);
52+
} catch (Exception $e) {
53+
$this->response->error($e->getMessage());
54+
}
55+
$this->out = $base;
56+
57+
$this->postPerform();
58+
}
59+
60+
}

0 commit comments

Comments
 (0)