compare object properties and show diff in PHP
Something like the following, which iterates through and does a recursive diff is the item in the array is itself an array could work:
Des similar work to array_diff, but it does a check to see if it is an array first (is_array) and if so, sets the diff for that key to be the diff for that array. Repeats recursively.
function recursive_array_diff($a1, $a2) { $r = array(); foreach ($a1 as $k => $v) { if (array_key_exists($k, $a2)) { if (is_array($v)) { $rad = recursive_array_diff($v, $a2[$k]); if (count($rad)) { $r[$k] = $rad; } } else { if ($v != $a2[$k]) { $r[$k] = $v; } } } else { $r[$k] = $v; } } return $r; }
It then works like this:
$obj1 = new StdClass; $obj1->prop = array(1,2);$obj2 = new StdClass; $obj2->prop = array(1,3);print_r(recursive_array_diff((array)$obj1, (array)$obj2));/* Output: Array ( [prop] => Array ( [1] => 2 ) )*/
My solution will recursively diff a stdClass and all it nested arrays and stdClass objects.It is meant to be used for comparison of rest api responses.
function objDiff($obj1, $obj2):array { $a1 = (array)$obj1; $a2 = (array)$obj2; return arrDiff($a1, $a2);}function arrDiff(array $a1, array $a2):array { $r = array(); foreach ($a1 as $k => $v) { if (array_key_exists($k, $a2)) { if ($v instanceof stdClass) { $rad = objDiff($v, $a2[$k]); if (count($rad)) { $r[$k] = $rad; } }else if (is_array($v)){ $rad = arrDiff($v, $a2[$k]); if (count($rad)) { $r[$k] = $rad; } // required to avoid rounding errors due to the // conversion from string representation to double } else if (is_double($v)){ if (abs($v - $a2[$k]) > 0.000000000001) { $r[$k] = array($v, $a2[$k]); } } else { if ($v != $a2[$k]) { $r[$k] = array($v, $a2[$k]); } } } else { $r[$k] = array($v, null); } } return $r; }
Here is a comparison function that I built using the pattern:
function objEq(stdClass $obj1, stdClass $obj2):bool { $a1 = (array)$obj1; $a2 = (array)$obj2; return arrEq($a1, $a2);}function arrEq(array $a1, array $a2):bool { foreach ($a1 as $k => $v) { if (array_key_exists($k, $a2)) { if ($v instanceof stdClass) { $r = objEq($v, $a2[$k]); if ($r === false) return false; }else if (is_array($v)){ $r = arrEq($v, $a2[$k]); if ($r === false) return false; } else if (is_double($v)){ // required to avoid rounding errors due to the // conversion from string representation to double if (abs($v - $a2[$k]) > 0.000000000001) { return false; } } else { if ($v != $a2[$k]) { return false; } } } else { return false; } } return true; }
Usage:
$apiResponse = apiCall(GET, $objId);$responseObj = json_decode($apiResponse);// do stuff ...if(!objEq($myObj, $responseObj) apiCall(PUT, $myObj, $objId);
Note that the apiCall function is just a mock to illustrate the concept.Also this solution is incomplete because it does not take into account any key->value pairs that are unique to obj2. In my use case this is not required and could be neglected.
NB: I borrowed heavily from Peter Hamiltons contribution. If you like what I did then please upvote his solution. Thanks!