您是否可以在PHP中使用模式或魔术方法来定义何时比较类的两个实例?
例如,在Java中,我可以轻松覆盖equals
方法,并创建一种自定义方式来检查和比较两个实例。
答案 0 :(得分:28)
总之一句话?没有。没有__equals
魔法。有一个完整的魔术方法列表in the manual。
你可以做到
$myObject1 == $myObject2
如果它们具有相同的属性和值,则认为它们相等,并且是同一类的实例。
我自己经常希望这种类型的方法,但我认为更有用的方法是__compare()
方法,可以为任何比较运算符调用<,>,==,== =,等已经存在PHP的内置类,如PHP internals wiki中所示,并且有一个例子说明它如何在PHPInternals book中实现: -
compare_objects
int (*compare)(zval *object1, zval *object2 TSRMLS_DC)
比较两个对象。用于运算符==,!=,<,>,⇐和> =。 实现应该遵循这些规则 - 对于共享相同比较处理程序的任何对象a,b和c:
我用来实现这一目标的一种方法是实现Comparable接口,例如: -
interface Comparable
{
/**
* @param Comparable $other
*
* @return Int -1, 0 or 1 Depending on result of comparison
*/
public function compareTo(Comparable $other);
}
可以在http://www.php.net/manual/en/language.oop5.php找到对象比较的详细信息以及其他与OOP相关的内容。
<击> This may be implemented in PHP 7 击>
现在有一个可以使用composer安装的实现。 https://github.com/Fleshgrinder/php-comparable
答案 1 :(得分: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
它并不是特别优雅,但应该让你上路。
安东尼。
答案 2 :(得分:4)
在大多数情况下,首先关闭==
运算符就足够了,尤其是在我们讨论值对象时。如果您希望能够将实例与标量值进行比较,请务必提供__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中没有),因为它会抱怨实例无法转换为所需的值。因此,您始终需要确保与之比较的值类型是字符串。
如果您想要更多,例如你想要将输入小写或处理浮点值直到达到一定的精度,你需要一个不同的方法。处理此问题的一种方法如下。
<?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;
}
}
按照惯例简单地遵循这些方法名称允许它适当地使用它们。甚至可以提供特征来在多个类中实现所需的默认行为。
答案 3 :(得分:1)
基本上,正如每个人所说,这样做:
$object1 == $object2
比较类型和属性。
但是我在这种情况下做的是,当我想个性化我的等式方法时,在我想要断言相等的类中实现魔术方法__toString()。
class Car {
private $name;
private $model;
...
public function __toString() {
return $this->name.", ".$this->model;
}
}
然后当我想进行比较时,我就这样做了:
$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;
}
}
答案 4 :(得分: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
它比较了在类中找到的第一个属性,在我的例子中是一个int值,它保存给定时间内的总秒数。 如果你将$ 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;
}
}