Skip to content

Commit 575513b

Browse files
Fix "diff" result for dates and other objects (#599)
Co-authored-by: Javier Spagnoletti <[email protected]>
1 parent 0b12efe commit 575513b

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

src/Utils/ArrayDiff.php

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public function diff($oldData, $newData)
3737
$old = \array_key_exists($field, $oldData) ? $oldData[$field] : null;
3838
$new = \array_key_exists($field, $newData) ? $newData[$field] : null;
3939

40-
if ($old === $new) {
40+
// If the values are objects, we will compare them by their properties.
41+
// This is necessary because the strict comparison operator (===) will return false if the objects are not the same instance.
42+
if ((\is_object($old) && \is_object($new) && $this->compareObjects($old, $new)) || ($old === $new)) {
4143
$row = ['old' => '', 'new' => '', 'same' => $old];
4244
} else {
4345
$row = ['old' => $old, 'new' => $new, 'same' => ''];
@@ -48,4 +50,42 @@ public function diff($oldData, $newData)
4850

4951
return $diff;
5052
}
53+
54+
/**
55+
* Compare the type and the property values of two objects.
56+
* Return true if they are the same, false otherwise.
57+
* If the type is the same and all properties are the same, this will return true, even if they are not the same instance.
58+
* This method is different from comparing two objects using ==,
59+
* because internally the strict comparison operator (===) is used to compare the properties.
60+
*
61+
* @see https://www.php.net/manual/en/language.oop5.object-comparison.php
62+
*/
63+
private function compareObjects(object $object1, object $object2): bool
64+
{
65+
// Check if the objects are of the same type.
66+
if ($object1::class !== $object2::class) {
67+
return false;
68+
}
69+
70+
// Check if all properties are the same.
71+
$obj1Properties = (array) $object1;
72+
$obj2Properties = (array) $object2;
73+
foreach ($obj1Properties as $key => $value) {
74+
if (!\array_key_exists($key, $obj2Properties)) {
75+
return false;
76+
}
77+
if (\is_object($value) && \is_object($obj2Properties[$key])) {
78+
if (!$this->compareObjects($value, $obj2Properties[$key])) {
79+
return false;
80+
}
81+
82+
continue;
83+
}
84+
if ($value !== $obj2Properties[$key]) {
85+
return false;
86+
}
87+
}
88+
89+
return true;
90+
}
5191
}

tests/Utils/ArrayDiffTest.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Sonata Project package.
7+
*
8+
* (c) Thomas Rabaix <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Sonata\EntityAuditBundle\Tests\Utils;
15+
16+
use PHPUnit\Framework\TestCase;
17+
use SimpleThings\EntityAudit\Utils\ArrayDiff;
18+
19+
final class ArrayDiffTest extends TestCase
20+
{
21+
public function testDiff(): void
22+
{
23+
$diff = new ArrayDiff();
24+
$array1 = ['one' => 'I', 'two' => '2'];
25+
$array2 = ['one' => 'I', 'two' => 'II'];
26+
$expected = ['one' => ['old' => '', 'new' => '', 'same' => 'I'], 'two' => ['old' => '2', 'new' => 'II', 'same' => '']];
27+
28+
$result = $diff->diff($array1, $array2);
29+
30+
static::assertSame($expected, $result);
31+
}
32+
33+
public function testDiffIsCaseSensitive(): void
34+
{
35+
$diff = new ArrayDiff();
36+
$array1 = ['one' => 'I', 'two' => 'ii'];
37+
$array2 = ['one' => 'I', 'two' => 'II'];
38+
$expected = ['one' => ['old' => '', 'new' => '', 'same' => 'I'], 'two' => ['old' => 'ii', 'new' => 'II', 'same' => '']];
39+
40+
$result = $diff->diff($array1, $array2);
41+
42+
static::assertSame($expected, $result);
43+
}
44+
45+
public function testDiffDate(): void
46+
{
47+
$diff = new ArrayDiff();
48+
49+
$dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000');
50+
$dateInstance2 = new \DateTimeImmutable('2014-01-01 00:00:00.000000');
51+
52+
$array1 = ['date' => $dateInstance1];
53+
$array2 = ['date' => $dateInstance2];
54+
$expected = ['date' => ['old' => '', 'new' => '', 'same' => $dateInstance1]];
55+
56+
$result = $diff->diff($array1, $array2);
57+
58+
static::assertSame($expected, $result);
59+
}
60+
61+
public function testDiffDateDifferent(): void
62+
{
63+
$diff = new ArrayDiff();
64+
65+
$dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000');
66+
$dateInstance2 = new \DateTimeImmutable('2014-01-02 00:00:00.000000');
67+
68+
$array1 = ['date' => $dateInstance1];
69+
$array2 = ['date' => $dateInstance2];
70+
$expected = ['date' => ['old' => $dateInstance1, 'new' => $dateInstance2, 'same' => '']];
71+
72+
$result = $diff->diff($array1, $array2);
73+
74+
static::assertSame($expected, $result);
75+
}
76+
77+
public function testDiffDateSameButTimezoneDifferent(): void
78+
{
79+
$diff = new ArrayDiff();
80+
81+
$dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000', new \DateTimeZone('Europe/Luxembourg'));
82+
$dateInstance2 = new \DateTimeImmutable('2014-01-01 00:00:00.000000', new \DateTimeZone('UTC'));
83+
84+
$array1 = ['date' => $dateInstance1];
85+
$array2 = ['date' => $dateInstance2];
86+
$expected = ['date' => ['old' => $dateInstance1, 'new' => $dateInstance2, 'same' => '']];
87+
88+
$result = $diff->diff($array1, $array2);
89+
90+
static::assertSame($expected, $result);
91+
}
92+
93+
public function testDiffObjectSame(): void
94+
{
95+
$diff = new ArrayDiff();
96+
$object1 = (object) ['one' => 'I', 'two' => 'II'];
97+
$object2 = (object) ['one' => 'I', 'two' => 'II'];
98+
$array1 = ['object' => $object1];
99+
$array2 = ['object' => $object2];
100+
$expected = ['object' => ['old' => '', 'new' => '', 'same' => $object1]];
101+
102+
$result = $diff->diff($array1, $array2);
103+
104+
static::assertSame($expected, $result);
105+
}
106+
107+
public function testDiffObjectDifferent(): void
108+
{
109+
$diff = new ArrayDiff();
110+
$object1 = (object) ['one' => 'I', 'two' => 'ii'];
111+
$object2 = (object) ['one' => 'I', 'two' => 'II'];
112+
$array1 = ['object' => $object1];
113+
$array2 = ['object' => $object2];
114+
$expected = ['object' => ['old' => $object1, 'new' => $object2, 'same' => '']];
115+
116+
$result = $diff->diff($array1, $array2);
117+
118+
static::assertSame($expected, $result);
119+
}
120+
}

0 commit comments

Comments
 (0)