web-dev-qa-db-fra.com

Existe-t-il une méthode __equals en PHP comme en Java?

Existe-t-il un modèle ou une méthode magique que vous pouvez utiliser dans PHP pour définir quand comparer deux instances d'une classe?

Par exemple, dans Java je pourrais facilement remplacer la méthode equals et créer un moyen personnalisé de vérifier et de comparer deux instances.

31
Simon

En un mot? Non. Il n'y a pas de méthode magique __equals. Il y a une liste complète des méthodes magiques dans le manuel .

Tu peux faire

$myObject1 == $myObject2

qui les considérera comme égaux s'ils ont les mêmes attributs et valeurs et sont des instances de la même classe.

J'ai souvent souhaité ce type de méthode moi-même, mais je pense qu'une méthode plus utile serait une méthode __compare() qui serait appelée pour tout opérateur de comparaison <,>, ==, ===, etc. il existe déjà pour les classes intégrées de PHP comme on peut le voir dans le PHP internals wiki et il y a un exemple de comment il pourrait être implémenté dans le PHPInternals book : -

compare_objects

int (*compare)(zval *object1, zval *object2 TSRMLS_DC)

Compare deux objets. Utilisé pour les opérateurs ==,! =, <,>, ⇐ et> =. Les implémentations doivent suivre ces règles - pour tous les objets a, b et c qui partagent le même gestionnaire de comparaison:

Une façon que j'ai utilisée pour y parvenir est d'implémenter une interface comparable, quelque chose comme: -

interface Comparable
{
    /**
     * @param Comparable $other
     * 
     * @return Int -1, 0 or 1 Depending on result of comparison
     */
    public function compareTo(Comparable $other);
}

Les détails de la comparaison d'objets, et tout le reste OOP en relation peut être trouvé ici http://www.php.net/manual/en/language.oop5.php .

Ceci peut être implémenté dans PHP 7 .

Il existe maintenant une implémentation de cela que vous pouvez installer à l'aide de composer. https://github.com/Fleshgrinder/php-comparable

28
vascowhite

Malheureusement non, mais vous pouvez très facilement reproduire quelque chose de proche. Par exemple:-

<?php
interface IComparable {
    public function compare(self $subject);
}

class Foo implements IComparable {
    public function compare(self $subject) {
        return $this->__toString() === $subject->__toString();
    }
    public function __toString() {
        return serialize($this);
    }
}

function compare(IComparable $a, IComparable $b) {
    return $a->compare($b);
}

$a = new Foo;
$b = new Foo;

var_dump(compare($a, $b)); //true

$a->name = 'A';
$b->name = 'B';

var_dump(compare($a, $b)); //false

Ce n'est pas particulièrement élégant, mais ça devrait vous aider sur votre chemin.

Anthony.

7
Anthony Sterling

Tout d'abord le == L'opérateur est suffisant dans la plupart des cas, surtout si nous parlons d'objets de valeur. Assurez-vous simplement de fournir un __toString méthode si vous voulez pouvoir comparer l'instance avec des valeurs scalaires.

<?php

final class ValueObject {

    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function __toString() {
        return (string) $this->value;
    }

}

$a = new ValueObject(0);
$b = new ValueObject(1);

var_dump(
    $a == $b,  // bool(false)
    $a == $a,  // bool(true)
    $b == $b,  // bool(true)
    $a == '0', // bool(true)
    $b == '1'  // bool(true)
);

Il y a un problème avec cela, vous ne pouvez pas comparer à un type scalaire autre que chaîne (du moins pas dans PHP 5 et 7) car il se plaindra que l'instance n'a pas pu être convertie à la valeur souhaitée Par conséquent, vous devez toujours vous assurer que le type de valeur auquel vous vous comparez est une chaîne.

Si vous voulez plus que cela, par exemple vous voulez réduire la valeur flottante en entrée ou la gérer jusqu'à une certaine précision, vous avez besoin d'une approche différente. Une façon de gérer cela est la suivante.

<?php

interface Comparable {

    function compareTo(Comparable $other): int;

}

function class_compare(Comparable $a, Comparable $b): int {
    return $a->compareTo($b);
}

final class C implements Comparable {

    private $value;

    public function __construct(int $value) {
        $this->value = $value;
    }

    public function compareTo(Comparable $other): int {
        assert($this instanceof $other && $other instanceof $this);

        return $this->value <=> $other->value;
    }

}

$c1 = new C(0);
$c2 = new C(0);

var_dump($c1->compareTo($c2)); // int(0)

$c0 = new C(0);
$c1 = new C(1);
$c2 = new C(2);

$actual = [$c2, $c1, $c0];
usort($actual, 'class_compare');

var_dump($actual === [$c0, $c1, $c2]); // bool(true)

Le assert est important car nous n'avons pas de génériques en PHP. C'est un état de choses assez triste et il n'y a pas de joli moyen de le mettre en œuvre.

Ce que j'ai tendance à faire est le suivant.

<?php

final class SomeClass {

    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public static function compare(SomeClass $a, SomeClass $b): int {
        return $a->compareTo($b);
    }

    public function compareTo(SomeClass $other): int {
        return $this->value <=> $other->value;
    }

    public function isEqual($other): bool {
        return $other instanceof $this && $other->value === $this->value;
    }

    public function isIdentical($other): bool {
        return $other instanceof $this && $this instanceof $other && $other->value === $this->value;
    }

}

Le simple fait de s'en tenir à ces noms de méthode par convention lui permet de les utiliser de manière appropriée. On pourrait même fournir des traits pour implémenter le comportement par défaut souhaité parmi plusieurs classes.

4
Fleshgrinder

Fondamentalement, comme tout le monde le dit, cela fera:

$object1 == $object2

Compare le type et les propriétés.


Mais ce que je fais dans ce cas, quand je veux personnaliser mes méthodes d'égalité, c'est implémenter la méthode magique __toString () dans les classes pour lesquelles je veux affirmer l'égalité.

class Car {
  private $name;
  private $model;
   ...
  public function __toString() {
     return $this->name.", ".$this->model;
  }
}

Et puis quand je veux faire la comparaison, je fais juste ceci:

$car1->toString() === $car2->toString()

Et cela se comparera si les deux instances ont les mêmes attributs.

L'autre option (comme les états de halfer dans les commentaires) consiste à implémenter une méthode égale qui affirme l'égalité d'une autre instance de la même classe. Par exemple:

class Car {
  private $name;
  private $model;
   ...
  public function equals(Car $anotherCar) {
         if($anotherCar->getName() !== $this->name) {
           return false;
         }

         if($anotherCar->getModel() !== $this->model) {
           return false;
         }
         ...
         return true;
  }
}
1
Tomas Prado

Si vous souhaitez comparer votre objet personnalisé, vous pouvez le faire comme ceci:

$time1 = new MyTimeClass("09:35:12");
$time2 = new MyTimeClass("09:36:09");

if($time1 > $time2) echo "Time1 is bigger";
else echo "Time2 is bigger";

//result: Time1 is bigger

Il compare la première propriété trouvée dans la classe, dans mon cas une valeur int qui contient le nombre total de secondes dans le temps donné. Si vous placez la propriété $ seconds sur le dessus, vous remarquerez qu'elle donne un 'Time1 est plus grand' inattendu.

class MyTimeClass {
    public $intValue;
    public $hours;
    public $minutes;
    public $seconds;

    public function __construct($str){
        $array = explode(":",$str);
        $this->hours = $array[0];
        $this->minutes = $array[1];
        $this->seconds = $array[2];
        $this->intValue = ($this->hours * 3600) + ($this->minutes * 60) + $this->seconds;
    }
}
0
Johan Velthuis