36

是否有可以在 PHP 中使用的模式或魔术方法来定义何时比较一个类的两个实例?

例如,在 Java 中,我可以轻松地覆盖该equals方法并创建一种自定义方式来检查和比较两个实例。

4

5 回答 5

34

一句话?不,没有__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

这可以在 PHP 7 中实现

现在有一个实现,您可以使用 composer 安装它。https://github.com/Fleshgrinder/php-comparable

于 2013-06-09T10:28:43.267 回答
8

遗憾的是没有,但你可以很容易地复制接近的东西。例如:-

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

它不是特别优雅,但应该让你上路。

安东尼。

于 2013-06-09T11:28:04.177 回答
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;
    }

}

只需按照惯例坚持这些方法名称,就可以适当地使用它们。甚至可以提供特征来在多个类中实现所需的默认行为。

于 2016-11-14T10:52:38.390 回答
2

基本上,正如每个人所说,这将做到:

$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;
  }
}
于 2013-06-09T10:34:38.437 回答
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: 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;
    }
}
于 2015-07-03T13:00:57.667 回答