Created
December 3, 2014 22:06
-
-
Save wrey75/c631f6fe9c975354aec7 to your computer and use it in GitHub Desktop.
A JSON Diff object
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* This class works on arrays, not on JSON strings | |
* directly. You have to use json_encode/json_decode | |
* for doing this. | |
* | |
* @author [email protected] | |
* | |
*/ | |
class JSONTools { | |
const OLD_ITEM = 'old'; | |
const NEW_ITEM = 'new'; | |
/** | |
* Compute the difference between $arr1 and $arr2. | |
* The result is an array (can be empty) giving the | |
* differences between the 2 arrays. | |
* | |
* Note the differences are expressed in a form you | |
* can use the resulting array to "patch" $arr1 to | |
* obtain $arr2. | |
* | |
* This is basically the same stuff than for "diff" | |
* command (from UNIX). | |
* | |
* @param array $arr1 the first array | |
* @param array $arr2 the second array | |
*/ | |
public static function diff($arr1, $arr2) { | |
$diff = array(); | |
// Check the similarities | |
foreach( $arr1 as $k1=>$v1 ){ | |
if( isset( $arr2[$k1]) ){ | |
$v2 = $arr2[$k1]; | |
if( is_array($v1) && is_array($v2) ){ | |
// 2 arrays: just go further... | |
// .. and explain it's an update! | |
$changes = self::diff($v1, $v2); | |
if( count($changes) > 0 ){ | |
// If we have no change, simply ignore | |
$diff[$k1] = array('upd' => $changes); | |
} | |
unset($arr2[$k1]); // don't forget | |
} | |
else if( $v2 === $v1 ){ | |
// unset the value on the second array | |
// for the "surplus" | |
unset( $arr2[$k1] ); | |
} | |
else { | |
// Don't mind if arrays or not. | |
$diff[$k1] = array( 'old' => $v1, 'new'=>$v2 ); | |
unset( $arr2[$k1] ); | |
} | |
} | |
else { | |
// remove information | |
$diff[$k1] = array( 'old' => $v1 ); | |
} | |
} | |
// Now, check for new stuff in $arr2 | |
reset( $arr2 ); // Don't argue it's unnecessary (even I believe you) | |
foreach( $arr2 as $k=>$v ){ | |
// OK, it is quite stupid my friend | |
$diff[$k] = array( 'new' => $v ); | |
} | |
return $diff; | |
} | |
/** | |
* Patching is so simple... | |
* | |
* @param unknown $arr | |
* @param unknown $patch | |
*/ | |
public static function patch($arr, $patch) { | |
$dest = $arr; | |
foreach ($patch as $k=>$v){ | |
// $k is the key to change | |
// $v contains 'old' and 'new'. | |
if( !is_array($v) ){ | |
error_log('$patch is a bad argument.'); | |
} | |
else if( isset( $v['upd'] )){ | |
// Update...! | |
$dest[$k] = self::patch($arr[$k], $v['upd']); | |
} | |
else if( !isset( $v['new'] )){ | |
// Remove the entry | |
unset( $dest[$k] ); | |
} | |
else { | |
// A new value (can be array by the way). | |
$dest[$k] = $v['new']; | |
} | |
} | |
return $dest; | |
} | |
} |
Pretty nice, but on line 36 you're using isset()
which fails if $arr2[$k1]
is null
, which is a possible value in JSON.
Changing the statement to array_key_exists($k1, $arr2)
will do the job correctly.
Anyway, thanks for this piece of code, especially the patch()
functionality s nice!
I have encoutered the same problem as Somrlik about the test at line 36. I an array key has a null value in both arrays, then the actual function will note the key as [upd], while it should be ignored because both keys, even if they have a null value, are equals. The suggested fix works perfectly.
So line 36 should be changed from :
if (isset($arr2[$k1])) {
to
if(array_key_exists ($k1, $arr2)){
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
super