Cómo comparar objectos en PHP

PHP ofrece una manera sencilla de comparar objectos usando los operadores de comparación (==) e identidad (===), si usamos el operador de comparación entonces los objetos son comparados en forma simple o sea 2 objectos son iguales si son instancias de la misma clase y tienen los mismos atributos (propiedades) y valores, si usamos el operador de identidad entonces 2 objetos son iguales solo si hacen referencia (o apuntan) a la misma instancia de la misma clase.

En ocaciones no basta con saber si 2 objetos son iguales o differentes sino que en el caso de que sean differentes necesitamos saber las differencias, podemos lograrlo creando una función que reciba como parámetros los objetos a comparar, obtenga las propiedades de los objetos usando Reflection itere por las propiedades de los objetos y compare los valores de las propiedades y luego retorne un arreglo con las differencias.

Pongamos lo comentado anteriormente en la siguiente clase:

<?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;
    }
}

Realizando algunas pruebas

<?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

Lecturas recomendadas


YouTube video

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.