2013-06-09 2 views
23

Есть ли шаблон или магический метод, который вы можете использовать в PHP, чтобы определить, когда сравнивать два экземпляра класса?Есть ли способ __equals в PHP, как есть в Java?

Например, в Java я мог бы легко переопределить метод equals и создать собственный способ проверки и сравнения двух экземпляров.

+0

Вы хотите сравнить объекты, являющиеся одним и тем же экземпляром экземпляра одного объекта из одного класса? Вы проверили [руководство] (http://php.net/manual/en/language.oop5.object-comparison.php)? – dbf

+0

Java не имеет магических методов, поэтому я не знаю, что именно вы спрашиваете. Конечно, вы можете просто добавить такие методы, как на Java. –

+2

Вместо того, чтобы сравнивать '$ ClassA === $ ClassB', просто реализуйте equals самостоятельно, так что вы можете сделать $ ClassA-> equals ($ ClassB)'. – halfer

ответ

21

Одним словом? Нет. Метод магии __equals. Существует полный список магических методов in the manual.

Вы можете сделать

$myObject1 == $myObject2 

, которые будут считать их равными, если они имеют одни и те же атрибуты и значения, и являются экземплярами одного и того же класса.

Я часто хотел, чтобы этот тип метода был сам, но я думаю, что более полезным будет метод __compare(), который будет вызываться для любого оператора сравнения <,>, ==, === и т. Д. Он уже существуют для встроенных классов PHP, как можно видеть в PHP internals wiki и есть пример того, как это может быть реализовано в PHPInternals book: -

compare_objects

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

сравнивает два объекта. Используется для операторов ==,! =, <,>, ⇐ и> =. Реализация должны следовать этим правилам - для любых объектов а, Ь и с, которые разделяют один и тот же сравнивать обработчик:

Один из способов я использовал для достижения этой цели является внедрением Сопоставимого интерфейса, что-то вроде: -

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

Детали сравнения объектов и все остальное ООП можно найти здесь http://www.php.net/manual/en/language.oop5.php.

This may be implemented in PHP 7.

+0

Второй аргумент неверен, поскольку сравнение должно возвращать -1 (меньше), 0 (равно) или 1 (больше). – Fleshgrinder

+0

@Fleshgrinder Я не уверен, что будет работать на PHP, как он сейчас стоит. Хотя я всегда открыт для изучения чего-то нового: O) – vascowhite

+0

Ознакомьтесь с любым методом сравнения в C, Java, ..., а также с любыми функциями и методами сравнения в самом PHP. Они всегда возвращают одно из этих чисел. Обратный вызов 'usort' также требуется для возврата одного из этих чисел. См. Мой добавленный ответ в этой теме. – Fleshgrinder

1

В принципе, как все говорят, это будет делать:

$object1 == $object2 

Сравнивает тип и свойства.


Но что делать в этом случае, когда я хочу, чтобы персонализировать свои методы равенства, это реализовать магический метод __toString() в классах, которые я хочу утверждать равенство.

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

И тогда, когда я хочу сделать Comparision я просто делаю это:

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

И это будет сравнить, если два экземпляра имеют одни и те же атрибуты.

Другой вариант (как состояния halfer в комментариях) реализует равный метод, который утверждает равенство другого экземпляра того же класса.Например:

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

К сожалению нет, но вы можете легко воспроизвести что-то близко. Например: -

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

Это не особенно элегантно, но должно быть на вашем пути.

Anthony.

+0

Это фактически закончится фатальной ошибкой из-за использования 'self', который указывает на' IComparable' в интерфейсе и 'Foo' в' Foo', и, таким образом, реализация не выполняет контракт. – Fleshgrinder

0

Если вы хотите сравнить свой собственный объект, вы можете сделать это следующим образом:

$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 

Он сравнивает первое свойство найденного в классе, в моем случае целочисленного значение, который содержит общее количество секунд в данное время. Если вы поместите свойство $ seconds сверху, вы заметите, что это дает неожиданный «Time1 больше».

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

Во-первых, оператор == достаточно в большинстве случаев, особенно если речь идет об объектах стоимостью. Просто не забудьте предоставить метод __toString, если вы хотите сравнить экземпляр со скалярными значениями.

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

Существует один глюк с этим, вы не можете сравнить скалярного типа, кроме строки (по крайней мере, не в PHP 5 и 7), потому что он будет жаловаться, что экземпляр не может быть преобразован в желаемое значение. Следовательно, вам всегда нужно убедиться, что тип значения, с которым вы сравниваете, является строкой.

Если вы хотите больше, например, вы хотите, чтобы ввести значение ввода или обрабатывать значение float до определенной точности, вам нужен другой подход. Один из способов справиться с этим заключается в следующем.

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

assert важно, так как у нас нет дженерики в PHP. Это довольно печальное состояние вещей, и нет никакого претворенного способа реализовать это.

Что я имею в виду, это следующее.

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

} 

Просто придерживаясь этих имен методов по соглашению, вы можете использовать их надлежащим образом. Можно даже выделить черты для реализации желаемого поведения по умолчанию для нескольких классов.

+0

Нужно ли утверждать как '$ this instanceof $ other', так и' $ other instanceof $ this'? Любые намеки на то, когда эти два сравнения приведут к другому результату? – GeorgeK

+1

Не нужно, это зависит от того, что вы хотите. '$ other instanceof $ this' означает, что' $ other' является либо '$ this', либо подклассом' $ this'. Следующая проверка запрещает подклассы, так как '$ this' также должен быть экземпляром' $ other'. – Fleshgrinder

Смежные вопросы