Skip to content

Instantly share code, notes, and snippets.

@wrey75
Created December 3, 2014 22:06
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save wrey75/c631f6fe9c975354aec7 to your computer and use it in GitHub Desktop.
Save wrey75/c631f6fe9c975354aec7 to your computer and use it in GitHub Desktop.
A JSON Diff object
<?php
/**
* This class works on arrays, not on JSON strings
* directly. You have to use json_encode/json_decode
* for doing this.
*
* @author wrey75@gmail.com
*
*/
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;
}
}
@eosforfly
Copy link

super

@Somrlik
Copy link

Somrlik commented Sep 8, 2017

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!

@albancGIT
Copy link

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