2013-06-09 10 views
23

Esiste un metodo o un metodo magico che è possibile utilizzare in PHP per definire quando confrontare due istanze di una classe?Esiste un metodo __equals in PHP come in Java?

Ad esempio, in Java è possibile sovrascrivere facilmente il metodo equals e creare un modo personalizzato di controllare e confrontare due istanze.

+0

Desiderate confrontare gli oggetti che sono la stessa istanza o due istanze di oggetti della stessa classe? Hai controllato [il manuale] (http://php.net/manual/en/language.oop5.object-comparison.php)? – dbf

+0

Java non ha metodi magici, quindi non so esattamente cosa stai chiedendo. Certo, puoi semplicemente aggiungere metodi come puoi in Java. –

+2

Invece di confrontare '$ ClassA === $ ClassB', implementa solo te stesso, quindi puoi fare' $ ClassA-> equals ($ ClassB) '. – halfer

risposta

21

In una parola? No. Non esiste il metodo magico __equals. C'è una lista completa dei metodi magici in the manual.

Si può fare

$myObject1 == $myObject2 

che considerarli uguali se hanno gli stessi attributi e valori, e sono istanze della stessa classe.

Ho desiderato spesso questo tipo di metodo, ma penso che uno più utile sarebbe un metodo __compare() che verrebbe chiamato per qualsiasi operatore di confronto <,>, ==, ===, ecc esso già esiste per classi incorporati PHP come si può vedere nella PHP internals wiki e v'è un esempio di come potrebbe essere implementata nelle PHPInternals book: -

compare_objects

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

confronta due oggetti. Utilizzato per gli operatori ==,! =, <,>, ⇐ e> =. Le implementazioni dovrebbero seguire queste regole - per tutti gli oggetti a, b e c che condividono lo stesso gestore confrontare:

Un modo che ho usato per raggiungere questo obiettivo è quello di implementare un'interfaccia Comparable, qualcosa di simile a: -

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

I dettagli del confronto degli oggetti e tutto il resto relativo all'OOP possono essere trovati qui http://www.php.net/manual/en/language.oop5.php.

This may be implemented in PHP 7.

+0

Il secondo argomento non è corretto poiché un confronto deve restituire -1 (minore), 0 (uguale) o 1 (più grande). – Fleshgrinder

+0

@Fleshgrinder Non sono sicuro che funzionerebbe in PHP così com'è attualmente. Anche se sono sempre aperto all'apprendimento di qualcosa di nuovo: O) – vascowhite

+0

Controlla qualsiasi metodo di confronto in C, Java, ... e qualsiasi funzione e metodo di confronto in PHP stesso. Restituiscono sempre uno di quei numeri. Il callback 'usort' è richiesto anche per restituire uno di quei numeri. Vedi la mia risposta aggiunta in questa discussione. – Fleshgrinder

1

In sostanza, come tutti dicono, questo farà:

$object1 == $object2 

confronto tra il tipo e le proprietà.


Ma quello che faccio in questi casi, quando ho voglia di personalizzare i miei metodi di uguaglianza, è implementare il metodo magico __toString() nelle classi che voglio affermare l'uguaglianza.

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

E poi, quando voglio fare il confronto Io faccio solo questo:

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

E che metterà a confronto, se le due istanze hanno gli stessi attributi.

L'altra opzione (come stati halfer nei commenti) implementa un metodo uguale che asserisce l'uguaglianza di un'altra istanza della stessa classe.Ad esempio:

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

Purtroppo no, ma è possibile replicare abbastanza facilmente qualcosa di simile. Ad esempio: -

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

Non è particolarmente elegante, ma dovrebbe farti strada.

Anthony.

+0

Questo in realtà finirà in un errore fatale a causa dell'uso di 'self' che punta a' IComparable' nell'interfaccia e 'Foo' a' Foo' e quindi l'implementazione non soddisfa il contratto. – Fleshgrinder

0

Se si desidera confrontare l'oggetto personalizzato, si può fare in questo modo:

$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 

Esso mette a confronto la prima proprietà trovata in classe, nel mio caso un valore int che contiene il numero totale di secondi nel tempo stabilito Se metti sopra la proprietà $ secondi, noterai che dà un inaspettato "Time1 è più grande".

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

Prima di tutto l'operatore == è sufficiente nella maggior parte dei casi, soprattutto se stiamo parlando di oggetti di valore. Assicurati di fornire un metodo __toString se desideri la possibilità di confrontare l'istanza con valori scalari.

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

C'è una Gotcha con questo, non è possibile confrontare di tipo scalare diverso stringa (almeno non in PHP 5 e 7) perché si lamentano che l'istanza non può essere convertito nel valore desiderato. Quindi, è sempre necessario assicurarsi che il tipo di valore che si confronta sia una stringa.

Se si desidera altro, ad es. vuoi mettere in minuscolo l'input o gestire il valore float fino a una certa precisione, hai bisogno di un approccio diverso. Un modo per gestire questo è il seguente.

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

Il assert è importante dal momento che non abbiamo generici in PHP. Questo è uno stato di cose piuttosto triste e non c'è un modo carino per implementarlo.

Quello che tendo a fare è il seguente.

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

} 

Basta aderire a questi nomi di metodo per convenzione che consente di utilizzarli in modo appropriato. Si potrebbero anche fornire tratti per implementare il comportamento predefinito desiderato tra più classi.

+0

E 'necessario affermare sia '$ questa istanza di $ altro' e' $ altra istanza di $ this'? Qualche suggerimento su quando questi due confronti darebbero risultati diversi? – GeorgeK

+1

Non necessario, dipende da ciò che si desidera. "$ other instanceof $ this" significa che "$ other" è "$ this" o una sottoclasse di "$ this". Il prossimo controllo non consente i sottolacass, poiché '$ this' deve essere anche un'istanza di' $ other'. – Fleshgrinder

Problemi correlati