@@ -9,6 +9,7 @@ class JsonDiff
9
9
protected $ exclude = [];
10
10
protected $ original ;
11
11
protected $ new ;
12
+ protected $ divider = '|| ' ;
12
13
13
14
/**
14
15
* JsonDiff constructor.
@@ -21,6 +22,8 @@ public function __construct($original = '')
21
22
}
22
23
23
24
/**
25
+ * Set indices to ignore when checking the diff
26
+ *
24
27
* @param string|array $value
25
28
*
26
29
* @return $this
@@ -35,30 +38,62 @@ public function exclude($value)
35
38
}
36
39
37
40
/**
41
+ * Set the divider used for the flatten and inflate operations
42
+ *
43
+ * @param null $divider
44
+ *
45
+ * @return $this
46
+ */
47
+ public function setDivider ($ divider = null )
48
+ {
49
+ $ this ->divider = $ divider ?: '|| ' ;
50
+
51
+ return $ this ;
52
+ }
53
+
54
+ /**
55
+ * Simple factory method for cleaner usage
56
+ *
57
+ * @param $original
58
+ *
59
+ * @return static
60
+ */
61
+ public static function original ($ original )
62
+ {
63
+ return new static ($ original );
64
+ }
65
+
66
+ /**
67
+ * Simple call to do everything tidyly
68
+ *
38
69
* @param string $original
39
70
* @param string $new
71
+ * @param array $exclude
40
72
*
41
73
* @return array
42
- * @throws \Exception
74
+ * @throws \Konsulting\Exceptions\JsonDecodeFailed
43
75
*/
44
- public function compare ($ original , $ new )
76
+ public static function compare ($ original , $ new, $ exclude = [] )
45
77
{
46
- $ this ->original = $ original ;
47
-
48
- return $ this ->compareTo ($ new );
78
+ return static ::original ($ original )->exclude ($ exclude )->compareTo ($ new );
49
79
}
50
80
51
81
/**
82
+ * Perform comparison of the original to this new JSON.
83
+ *
52
84
* @param string $new
53
85
*
54
- * @return array
86
+ * @return JsonDiffResult
55
87
* @throws \Konsulting\Exceptions\JsonDecodeFailed
56
88
*/
57
89
public function compareTo ($ new )
58
90
{
59
91
$ original = $ this ->decode ($ this ->original );
60
92
$ new = $ this ->decode ($ new );
61
93
94
+ // we flatten the whole array into a single level array with the
95
+ // indices representing the full depth, this lets us diff in
96
+ // a good level of detail.
62
97
$ flatOriginal = $ this ->flatten ($ original );
63
98
$ flatNew = $ this ->flatten ($ new );
64
99
@@ -68,17 +103,12 @@ public function compareTo($new)
68
103
$ added = array_diff_assoc ($ flatNew , $ flatOriginal );
69
104
$ removed = array_diff_assoc ($ flatOriginal , $ flatNew );
70
105
71
- $ diff = [
72
- 'added ' => $ this ->inflate ($ added ),
73
- 'removed ' => $ this ->inflate ($ removed ),
74
- ];
75
-
76
- $ changed = ! empty ($ added ) || ! empty ($ removed );
77
-
78
- return compact ('original ' , 'new ' , 'diff ' , 'changed ' );
106
+ return new JsonDiffResult ($ original , $ new , $ this ->inflate ($ added ), $ this ->inflate ($ removed ));
79
107
}
80
108
81
109
/**
110
+ * Try to decode the JSON we were passed
111
+ *
82
112
* @param $json
83
113
*
84
114
* @return array
@@ -96,6 +126,8 @@ public function decode($json)
96
126
}
97
127
98
128
/**
129
+ * Strip data that we want to ignore for diff-ing. It is a deep operation, digging into the data.
130
+ *
99
131
* @param array $columns
100
132
* @param array $array
101
133
*
@@ -119,60 +151,76 @@ public function stripColumns($columns = [], $array = [])
119
151
}
120
152
121
153
/**
122
- * @param $arr
154
+ * Flatten the json into a single level array with keys that represent the nested data positions.
155
+ *
156
+ * @param $toFlatten
123
157
* @param string $base
124
- * @param string $divider_char
125
158
*
126
159
* @return array
127
160
*/
128
- public function flatten ($ arr , $ base = "" , $ divider_char = " || " )
161
+ public function flatten ($ toFlatten , $ base = "" )
129
162
{
130
- $ ret = [];
131
- if (is_array ($ arr )) {
132
- foreach ($ arr as $ k => $ v ) {
133
- if (is_array ($ v )) {
134
- $ tmp_array = $ this ->flatten ($ v , $ base .$ k .$ divider_char , $ divider_char );
135
- $ ret = array_merge ($ ret , $ tmp_array );
136
- } else {
137
- $ ret [$ base .$ k ] = $ v ;
138
- }
139
- }
163
+ if (! is_array ($ toFlatten )) {
164
+ return $ toFlatten ;
165
+ }
166
+
167
+ $ flattened = [];
168
+ foreach ($ toFlatten as $ key => $ value ) {
169
+ $ flattenedValue = $ this ->flatten ($ value , $ base .$ key .$ this ->divider );
170
+
171
+ $ flattened = array_merge (
172
+ $ flattened ,
173
+ is_array ($ flattenedValue ) ? $ flattenedValue : [$ base .$ key => $ flattenedValue ]
174
+ );
140
175
}
141
- return $ ret ;
176
+
177
+ return $ flattened ;
142
178
}
143
179
144
180
/**
145
- * @param $arr
146
- * @param string $divider_char
181
+ * Inflate the single level array back up to a multi-dimensional PHP array
182
+ *
183
+ * @param $toInflate
147
184
*
148
- * @return array|bool
185
+ * @return mixed
149
186
*/
150
- public function inflate ($ arr , $ divider_char = " || " )
187
+ public function inflate ($ toInflate )
151
188
{
152
- if (!is_array ($ arr )) {
153
- return false ;
189
+ if (!is_array ($ toInflate )) {
190
+ return $ toInflate ;
154
191
}
155
192
156
- $ split = '/ ' . preg_quote ($ divider_char , '/ ' ) . '/ ' ;
157
-
158
- $ ret = [];
159
- foreach ($ arr as $ key => $ val ) {
160
- $ parts = preg_split ($ split , $ key , -1 , PREG_SPLIT_NO_EMPTY );
161
- $ leafpart = array_pop ($ parts );
162
- $ parent = &$ ret ;
163
- foreach ($ parts as $ part ) {
164
- if (!isset ($ parent [$ part ])) {
165
- $ parent [$ part ] = [];
166
- } elseif (!is_array ($ parent [$ part ])) {
167
- $ parent [$ part ] = [];
168
- }
169
- $ parent = &$ parent [$ part ];
170
- }
193
+ $ inflated = [];
194
+
195
+ foreach ($ toInflate as $ complexKey => $ value ) {
196
+ $ this ->inflateByComplexKey ($ inflated , $ complexKey , $ value );
197
+ }
171
198
172
- if (empty ($ parent [$ leafpart ])) {
173
- $ parent [$ leafpart ] = $ val ;
199
+ return $ inflated ;
200
+ }
201
+
202
+ /**
203
+ * Add a value to the array, by working through the nesting and popping the
204
+ * value in the correct place. The array is passed by reference and
205
+ * worked on directly.
206
+ *
207
+ * @param $inflated
208
+ * @param $complexKey
209
+ * @param $value
210
+ */
211
+ protected function inflateByComplexKey (&$ inflated , $ complexKey , $ value )
212
+ {
213
+ $ divider = '/ ' . preg_quote ($ this ->divider , '/ ' ) . '/ ' ;
214
+ $ keys = preg_split ($ divider , $ complexKey , -1 , PREG_SPLIT_NO_EMPTY );
215
+ $ finalKey = array_pop ($ keys );
216
+
217
+ foreach ($ keys as $ key ) {
218
+ if (!isset ($ inflated [$ key ])) {
219
+ $ inflated [$ key ] = [];
174
220
}
221
+ $ inflated = &$ inflated [$ key ];
175
222
}
176
- return $ ret ;
223
+
224
+ $ inflated [$ finalKey ] = $ value ;
177
225
}
178
226
}
0 commit comments