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!