Is there a __equals method in PHP like there is in Java?
Basically, as everyone says, this will do:
$object1 == $object2
Compares type and properties.
But what I do in this cases, when I want to personalize my equality methods, is implement the magic method __toString() in the classes I want to assert equality.
class Car {
private $name;
private $model;
...
public function __toString() {
return $this->name.", ".$this->model;
}
}
And then when I want to do the comparision I just do this:
$car1->toString() === $car2->toString()
And that will compare if the two instances have the same attributes.
The other option (as halfer states in the comments) is implement an equal method that asserts equality of another instance of the same class. For example:
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;
}
}
Sadly not, but you can quite easily replicate something close. For example:-
<?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
It's not particularly elegant, but should get you on your way.
Anthony.
In a word? No. There is no __equals
magic method. There is a complete list of the magic methods in the manual.
You can do
$myObject1 == $myObject2
which will consider them equal if they have the same attributes and values, and are instances of the same class.
I have often wished for this type of method myself, but I think that a more useful one would be a __compare()
method which would be called for any comparison operator <, >, ==, ===, etc it already exist for PHP's inbuilt classes as can be seen in the PHP internals wiki and there is an example of how it could be implemented in the PHPInternals book:-
compare_objects
int (*compare)(zval *object1, zval *object2 TSRMLS_DC)
Compares two objects. Used for the operators ==, !=, <, >, ⇐ and >=. The implementations should follow these rules – for any objects a, b and c that share the same compare handler:
One way I have used to achieve this is to implement a Comparable interface, something like:-
interface Comparable
{
/**
* @param Comparable $other
*
* @return Int -1, 0 or 1 Depending on result of comparison
*/
public function compareTo(Comparable $other);
}
The details of object comparison, and everything else OOP related can be found here http://www.php.net/manual/en/language.oop5.php.
This may be implemented in PHP 7.
There is now an implementation of this that you can install using composer. https://github.com/Fleshgrinder/php-comparable
First off the ==
operator is sufficient in most cases, especially if we are talking about value objects. Just be sure to provide a __toString
method if you want the ability to compare the instance with scalar values.
<?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)
);
There is one gotcha with this, you cannot compare to scalar type other than string (at least not in PHP 5 and 7) because it will complain that the instance could not be converted to the desired value. Hence, you always need to ensure that the value type you compare to is a string.
If you want more than that, e.g. you want to lowercase the input or handle float value up until a certain accuracy, you need a different approach. One way to handle this is as follows.
<?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
is important since we have no generics in PHP. This is a pretty sad state of things and there is no pretty way to implement this.
What I tend to do is the following.
<?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;
}
}
Simply sticking to these method names by convention allows it to use them appropriately. One could even provide traits to implement the desired default behavior among multiple classes.