Сравнить свойства объекта и показать diff в PHP
Я ищу способ показать мне разные свойства/значения из заданных объектов...
$obj1 = new StdClass; $obj1->prop = 1;
$obj2 = new StdClass; $obj2->prop = 2;
var_dump(array_diff((array)$obj1, (array)$obj2));
//output array(1) { ["prop"]=> int(1) }
Это работает очень хорошо, так как долго свойство не является объектом или массивом.
$obj1 = new StdClass; $obj1->prop = array(1,2);
$obj2 = new StdClass; $obj2->prop = array(1,3);
var_dump(array_diff((array)$obj1, (array)$obj2))
// Output array(0) { }
// Expected output - array { ["prop"]=> array { [1]=> int(2) } }
Есть ли способ избавиться от этого, даже если свойство является другим объектом?!
Ответы
Ответ 1
Что-то вроде следующего, которое выполняет итерацию через рекурсивный diff и выполняет рекурсивный diff, элемент в массиве сам по себе может работать:
Аналогичная работа с массивом_дифф, но он проверяет, является ли это первым массивом (is_array), и если это так, задает diff для этого ключа как diff для этого массива. Повторяется рекурсивно.
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;
}
Затем он работает следующим образом:
$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
)
)
*/
Ответ 2
Мое решение будет рекурсивно различать stdClass и все вложенные в него массивы и объекты stdClass. Он предназначен для сравнения ответов остальных API.
function objDiff(stdClass $obj1, stdClass $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;
}
Вот функция сравнения, которую я построил с использованием шаблона:
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;
}
Использование:
$apiResponse = apiCall(GET, $objId);
$responseObj = json_decode($apiResponse);
// do stuff ...
if(!objEq($myObj, $responseObj) apiCall(PUT, $myObj, $objId);
Обратите внимание, что функция apiCall - всего лишь макет, чтобы проиллюстрировать концепцию. Также это решение является неполным, потому что оно не учитывает пары значений key->, которые являются уникальными для obj2. В моем случае это не требуется и им можно пренебречь.
NB: я сильно позаимствовал у Питера Хэмилтона. Если вам нравится то, что я сделал, пожалуйста, проголосуйте за его решение. Спасибо!