How to compare objects in PHP

PHP offers a simple way to compare objects using the comparison (==) and identity (===) operators.

When using the comparison operator (==), object variables are compared in a simple manner: Two object instances are equal if they have the same attributes and values and are instances of the same class.

When using the identity operator (===), object variables are identical if and only if they refer to the same instance of the same class.

Sometimes it is not enough to know if 2 objects are equals or different else we need to know the differences, we can achieve this by creating a function that receives the objects to be compared as parameters, obtaining the properties of the objects using Reflection iterate through the properties of the objects and compare the values of properties and then return an array with the differences.

Let’s put on in the following class:

<?php
namespace Comparator;

use ReflectionObject;
use InvalidArgumentException;

class ObjectComparator
{

    /**
     * Compare 2 objects
     *
     * @param $o1
     * @param $o2
     * @param $strict Compare in simple (==) or in strict way (===)
     * @return true objects are equals, false objects are differents
     */
    public static function equal($o1, $o2, $strict = false)
    {
        return $strict ? $o1 === $o2 : $o1 == $o2;
    }

    /**
     * Find the differences between 2 objects using Reflection.
     *
     * @param $o1
     * @param $o2
     * @return array Properties that have changed
     * @throws InvalidArgumentException
     */
    public static function diff($o1, $o2)
    {
        if (!is_object($o1) || !is_object($o2)) {
            throw new InvalidArgumentException("Parameters should be of object type!");
        }

        $diff = [];
        if (get_class($o1) == get_class($o2)) {
            $o1Properties = (new ReflectionObject($o1))->getProperties();
            $o2Reflected = new ReflectionObject($o2);

            foreach ($o1Properties as $o1Property) {
                $o2Property = $o2Reflected->getProperty($o1Property->getName());
                // Mark private properties as accessible only for reflected class
                $o1Property->setAccessible(true);
                $o2Property->setAccessible(true);
                if (($oldValue = $o1Property->getValue($o1)) != ($newValue = $o2Property->getValue($o2))) {
                    $diff[$o1Property->getName()] = [
                        'old_value' => $oldValue,
                        'new_value' => $newValue
                    ];
                }
            }
        }

        return $diff;
    }
}

Testing…

<?php
include __DIR__ . '/ObjectComparator.php';

use Comparator\ObjectComparator as Comparator;

class Person
{
    public $name = '';
    private $age = 0;

    public function setAge($age)
    {
        $this->age = $age;
    }
}

function print_ln() {
    return php_sapi_name() == 'cli' ? PHP_EOL : nl2br(PHP_EOL);
}

$p1 = new Person();
$p2 = new Person();


echo '1. p1 == p2 => ', Comparator::equal($p1, $p2) ? 'Yes' : 'No', print_ln(); // Print Yes

$p1->name = 'Juan';

echo '2. p1 == p2 => ', Comparator::equal($p1, $p2) ? 'Yes' : 'No', print_ln();  // Print No

$p2->name = 'Juan';

echo '3. p1 == p2 => ', Comparator::equal($p1, $p2) ? 'Yes' : 'No', print_ln(); // Print Yes

$p2->setAge(20);

echo '4. p1 == p2 => ', Comparator::equal($p1, $p2) ? 'Yes' : 'No', print_ln(); // Print No

$p1->setAge(20);

echo '5. p1 == p2 => ', Comparator::equal($p1, $p2) ? 'Yes' : 'No', print_ln(); // Print Yes

$p2 = clone $p1;
echo '6. p1 == p2 => ', Comparator::equal($p1, $p2, true) ? 'Yes' : 'No', print_ln(); // Print No

$p2 = $p1;
echo '7. p1 == p2 => ', Comparator::equal($p1, $p2, true) ? 'Yes' : 'No', print_ln(); // Print Yes

$p3 = new Person();
$p3->name = 'Pepe';
$p3->setAge(50);

echo '8. Get the differences for p1, p3', print_ln();
foreach (Comparator::diff($p1, $p3) as $property => $diff) {
    echo $property, ' => ', 'old value: ', $diff['old_value'], ', new value: ', $diff['new_value'], print_ln();
}

// Print
// Get the differences for p1, p3
// name => old value: Juan, new value: Pepe
// age => old value: 20, new value: 50

Further readings


YouTube video

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.