是否有可以在 PHP 中使用的模式或魔术方法来定义何时比较一个类的两个实例?
例如,在 Java 中,我可以轻松地覆盖该equals
方法并创建一种自定义方式来检查和比较两个实例。
一句话?不,没有__equals
神奇的方法。手册中有完整的魔法方法列表。
你可以做
$myObject1 == $myObject2
如果它们具有相同的属性和值,并且是同一类的实例,它将认为它们相等。
我自己也经常希望使用这种类型的方法,但我认为更有用的__compare()
方法是任何比较运算符 <、>、==、=== 等都可以调用的方法,它已经存在于 PHP 的内置类中正如可以在PHP internals wiki中看到的那样,在PHPInternals 书中有一个如何实现它的示例:-
比较对象
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);
}
对象比较的详细信息以及与 OOP 相关的所有其他内容都可以在此处找到http://www.php.net/manual/en/language.oop5.php。
现在有一个实现,您可以使用 composer 安装它。https://github.com/Fleshgrinder/php-comparable
遗憾的是没有,但你可以很容易地复制接近的东西。例如:-
<?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
它不是特别优雅,但应该让你上路。
安东尼。
首先,==
在大多数情况下,运算符就足够了,尤其是当我们谈论值对象时。__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;
}
}
只需按照惯例坚持这些方法名称,就可以适当地使用它们。甚至可以提供特征来在多个类中实现所需的默认行为。
基本上,正如每个人所说,这将做到:
$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;
}
}
如果你想比较你的自定义对象,你可以这样做:
$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: Time2 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;
}
}