2013-06-09 9 views
23

Czy istnieje metoda wzorca lub magii, którą można użyć w PHP, aby określić, kiedy porównać dwa wystąpienia klasy?Czy istnieje metoda __equals w PHP, tak jak w Javie?

Na przykład w Javie mogłem z łatwością zastąpić metodę equals i utworzyć niestandardowy sposób sprawdzania i porównywania dwóch instancji.

+0

Czy chcesz porównać obiekty, które są tą samą instancją lub dwiema instancjami obiektu z tej samej klasy? Czy sprawdziłeś [podręcznik] (http://php.net/manual/en/language.oop5.object-comparison.php)? – dbf

+0

Java nie ma magicznych metod, więc nie wiem o co dokładnie pytasz. Oczywiście, możesz po prostu dodać metody takie jak w Javie. –

+2

Zamiast porównywać '$ ClassA === $ ClassB', po prostu zaimplementuj equals, więc możesz zrobić' $ ClassA-> equals ($ ClassB) '. – halfer

Odpowiedz

21

Jednym słowem? Nie. Nie ma żadnej magicznej metody. Istnieje pełna lista magicznych metod in the manual.

Można zrobić

$myObject1 == $myObject2 

które uważają je za równe, jeśli mają te same atrybuty i wartości, a są instancjami tej samej klasy.

Ja sam często chciałem tego typu metody, ale myślę, że bardziej przydatna byłaby metoda __compare(), która byłaby wywoływana dla każdego operatora porównania <,>, ==, ===, itp. istnieją dla klas wbudowanych PHP, jak można zobaczyć w PHP internals wiki i jest przykładem tego, jak to może być realizowane w PHPInternals book: -

compare_objects

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

porównuje dwa obiekty. Używane dla operatorów ==,! =, <,>, ⇐ i> =. Implementacje powinny przestrzegać tych zasad - dla dowolnych obiektów A, B i C, które mają ten sam porównanie Handler:

Jednym ze sposobów użyłem do osiągnięcia tego celu jest wdrożenie porównywalnej interfejs, coś takiego: -

interface Comparable 
{ 
    /** 
    * @param Comparable $other 
    * @param String $comparison any of ==, <, >, =<, >=, etc 
    * @return Bool true | false depending on result of comparison 
    */ 
    public function compareTo(Comparable $other, $comparison); 
} 

Szczegóły dotyczące porównywania obiektów i wszystkiego innego związanego z tym tematem można znaleźć tutaj http://www.php.net/manual/en/language.oop5.php.

This may be implemented in PHP 7.

+0

Drugi argument jest niepoprawny, ponieważ porównanie powinno zwrócić -1 (mniejsze), 0 (równe) lub 1 (większe). – Fleshgrinder

+0

@ Fleshgrinder Nie jestem pewien, czy działałoby w PHP tak jak obecnie. Chociaż zawsze jestem otwarty na uczenie się czegoś nowego: O) – vascowhite

+0

Sprawdź dowolną metodę porównywania w C, Java, ... oraz każdą z funkcji i metod porównywania w samym PHP. Zawsze zwracają jedną z tych liczb. Wywołanie 'usort' jest również wymagane, aby zwrócić jedną z tych liczb. Zobacz moją dodaną odpowiedź w tym wątku. – Fleshgrinder

1

Zasadniczo, jak każdy mówi, to zrobi:

$object1 == $object2 

Porównuje rodzaj i właściwości.


Ale co zrobić w takich przypadkach, gdy chcę, aby spersonalizować moje metody równości, jest wdrożenie magiczną metodę __toString() w klasach I chcą dochodzić równości.

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

I wtedy, kiedy chcę zrobić porównania po prostu to zrobić:

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

I że jeśli będzie porównać dwa przypadki mają te same atrybuty.

Inną opcją (jak stwierdza stan Halfera w komentarzach) jest zastosowanie równej metody, która zapewnia równość innej instancji tej samej klasy.Na przykład:

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

Niestety nie, ale można dość łatwo powielić coś bliskiego. Na przykład: -

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

To nie jest szczególnie eleganckie, ale powinno Cię zabić.

Anthony.

+0

Spowoduje to błąd krytyczny ze względu na użycie 'self', które wskazuje na" IComparable "w interfejsie i' Foo' w 'Foo', a zatem implementacja nie spełnia umowy. – Fleshgrinder

0

Jeśli chcesz porównać swój własny obiekt, możesz zrobić to tak:

$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 

Porównuje Pierwszy obiekt znajdujący się w klasie, w moim przypadku wartość int, który posiada całkowitą liczbę sekund w danym czasie. Jeśli umieścisz właściwość $ seconds na wierzchu, zauważysz, że daje ona nieoczekiwany "Czas1 jest większy".

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

Po pierwsze od operatora == jest wystarczająca w większości przypadków, szczególnie jeśli mówimy o obiektach wartości. Po prostu pamiętaj o podaniu metody __toString, jeśli chcesz porównać instancję z wartościami skalarnymi.

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

Jest jedna haczyka z tym, nie można porównać do typu skalarnego innego niż ciąg (przynajmniej nie w PHP 5 i 7), ponieważ będzie narzekać, że instancja nie może być zamieniony na żądaną wartość. Dlatego zawsze musisz upewnić się, że typ wartości, który porównujesz, jest ciągiem.

Jeśli chcesz uzyskać więcej, np. chcesz wprowadzać małe litery w danych wejściowych lub obsługiwać wartość zmiennoprzecinkową do pewnej dokładności, potrzebujesz innego podejścia. Jednym ze sposobów radzenia sobie z tym jest:

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

The assert jest ważny, ponieważ nie mamy generycznych w PHP. Jest to dość smutny stan rzeczy i nie ma łatwego sposobu na jego wdrożenie.

Co mam tendencję do zrobienia jest następujące.

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

} 

Po prostu trzymanie się tych nazw metod zgodnie z konwencją pozwala na ich właściwe użycie. Można nawet podać cechy, aby zaimplementować pożądane domyślne zachowanie wśród wielu klas.

+0

Czy konieczne jest potwierdzenie zarówno '$ this instanceof $ other' oraz' $ other instanceof $ this'? Jakieś wskazówki, kiedy te dwa porównania przyniosłyby inny rezultat? – GeorgeK

+1

Niepotrzebne, zależy to od tego, co chcesz. '$ other instanceof $ this' oznacza, że' $ other' jest albo '$ this' lub podklasą' $ this'. Następne sprawdzenie unieważnia sublcasses, ponieważ '$ this' musi również być instancją' $ other'. – Fleshgrinder