diff --git a/src/Utils/ArrayDiff.php b/src/Utils/ArrayDiff.php index 1ad6f1bb..8414d023 100644 --- a/src/Utils/ArrayDiff.php +++ b/src/Utils/ArrayDiff.php @@ -37,7 +37,9 @@ public function diff($oldData, $newData) $old = \array_key_exists($field, $oldData) ? $oldData[$field] : null; $new = \array_key_exists($field, $newData) ? $newData[$field] : null; - if ($old === $new) { + // If the values are objects, we will compare them by their properties. + // This is necessary because the strict comparison operator (===) will return false if the objects are not the same instance. + if ((\is_object($old) && \is_object($new) && $this->compareObjects($old, $new)) || ($old === $new)) { $row = ['old' => '', 'new' => '', 'same' => $old]; } else { $row = ['old' => $old, 'new' => $new, 'same' => '']; @@ -48,4 +50,42 @@ public function diff($oldData, $newData) return $diff; } + + /** + * Compare the type and the property values of two objects. + * Return true if they are the same, false otherwise. + * If the type is the same and all properties are the same, this will return true, even if they are not the same instance. + * This method is different from comparing two objects using ==, + * because internally the strict comparison operator (===) is used to compare the properties. + * + * @see https://www.php.net/manual/en/language.oop5.object-comparison.php + */ + private function compareObjects(object $object1, object $object2): bool + { + // Check if the objects are of the same type. + if ($object1::class !== $object2::class) { + return false; + } + + // Check if all properties are the same. + $obj1Properties = (array) $object1; + $obj2Properties = (array) $object2; + foreach ($obj1Properties as $key => $value) { + if (!\array_key_exists($key, $obj2Properties)) { + return false; + } + if (\is_object($value) && \is_object($obj2Properties[$key])) { + if (!$this->compareObjects($value, $obj2Properties[$key])) { + return false; + } + + continue; + } + if ($value !== $obj2Properties[$key]) { + return false; + } + } + + return true; + } } diff --git a/tests/Utils/ArrayDiffTest.php b/tests/Utils/ArrayDiffTest.php new file mode 100644 index 00000000..b0496838 --- /dev/null +++ b/tests/Utils/ArrayDiffTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\EntityAuditBundle\Tests\Utils; + +use PHPUnit\Framework\TestCase; +use SimpleThings\EntityAudit\Utils\ArrayDiff; + +final class ArrayDiffTest extends TestCase +{ + public function testDiff(): void + { + $diff = new ArrayDiff(); + $array1 = ['one' => 'I', 'two' => '2']; + $array2 = ['one' => 'I', 'two' => 'II']; + $expected = ['one' => ['old' => '', 'new' => '', 'same' => 'I'], 'two' => ['old' => '2', 'new' => 'II', 'same' => '']]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffIsCaseSensitive(): void + { + $diff = new ArrayDiff(); + $array1 = ['one' => 'I', 'two' => 'ii']; + $array2 = ['one' => 'I', 'two' => 'II']; + $expected = ['one' => ['old' => '', 'new' => '', 'same' => 'I'], 'two' => ['old' => 'ii', 'new' => 'II', 'same' => '']]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffDate(): void + { + $diff = new ArrayDiff(); + + $dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000'); + $dateInstance2 = new \DateTimeImmutable('2014-01-01 00:00:00.000000'); + + $array1 = ['date' => $dateInstance1]; + $array2 = ['date' => $dateInstance2]; + $expected = ['date' => ['old' => '', 'new' => '', 'same' => $dateInstance1]]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffDateDifferent(): void + { + $diff = new ArrayDiff(); + + $dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000'); + $dateInstance2 = new \DateTimeImmutable('2014-01-02 00:00:00.000000'); + + $array1 = ['date' => $dateInstance1]; + $array2 = ['date' => $dateInstance2]; + $expected = ['date' => ['old' => $dateInstance1, 'new' => $dateInstance2, 'same' => '']]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffDateSameButTimezoneDifferent(): void + { + $diff = new ArrayDiff(); + + $dateInstance1 = new \DateTimeImmutable('2014-01-01 00:00:00.000000', new \DateTimeZone('Europe/Luxembourg')); + $dateInstance2 = new \DateTimeImmutable('2014-01-01 00:00:00.000000', new \DateTimeZone('UTC')); + + $array1 = ['date' => $dateInstance1]; + $array2 = ['date' => $dateInstance2]; + $expected = ['date' => ['old' => $dateInstance1, 'new' => $dateInstance2, 'same' => '']]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffObjectSame(): void + { + $diff = new ArrayDiff(); + $object1 = (object) ['one' => 'I', 'two' => 'II']; + $object2 = (object) ['one' => 'I', 'two' => 'II']; + $array1 = ['object' => $object1]; + $array2 = ['object' => $object2]; + $expected = ['object' => ['old' => '', 'new' => '', 'same' => $object1]]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } + + public function testDiffObjectDifferent(): void + { + $diff = new ArrayDiff(); + $object1 = (object) ['one' => 'I', 'two' => 'ii']; + $object2 = (object) ['one' => 'I', 'two' => 'II']; + $array1 = ['object' => $object1]; + $array2 = ['object' => $object2]; + $expected = ['object' => ['old' => $object1, 'new' => $object2, 'same' => '']]; + + $result = $diff->diff($array1, $array2); + + static::assertSame($expected, $result); + } +}