【问题标题】:What's the fastest way to compare two objects in PHP?在 PHP 中比较两个对象的最快方法是什么?
【发布时间】:2012-03-26 05:14:26
【问题描述】:

假设我有一个对象——在这种情况下是一个用户对象——我希望能够使用一个单独的类来跟踪更改。 User 对象不应该以任何方式改变它的行为来发生这种情况。

因此,我的单独类创建了它的“干净”副本,将其存储在本地某个位置,然后可以将 User 对象与原始版本进行比较,以查看在其生命周期内是否有任何变化。

是否有函数、模式或任何东西可以快速比较用户对象的两个版本?

选项 1 也许我可以将每个版本序列化,然后直接比较,或者将它们散列并比较?

选项 2 也许我应该简单地创建一个 ReflectionClass,遍历该类的每个属性,看看这两个版本是否具有相同的属性值?

选项 3 也许有一个简单的原生函数,比如objects_are_equal($object1,$object2);

最快的方法是什么?

【问题讨论】:

  • 嗯,我知道我可以序列化这两个对象,并比较哈希值或其他东西....这不是我是否可以,而是我如何快速做到这一点?我会调整问题以反映这种精神......

标签: php object


【解决方案1】:

在不修改对象的情况下访问对象的所有(公共、受保护和私有)属性的唯一方法是通过reflection。如果您愿意,您可以在修改对象之前clone 对象,然后通过使用反射函数循环遍历两个对象之一的不同属性并比较值来比较它们。这将有效地告诉您哪些属性发生了变化。

如果您只想查看对象是否已更改,则可以使用 == 或 === 运算符。查看 PHP 文档的 comparing objects 部分。

也就是说,在更改时跟踪更改的内容不是更容易吗?

【讨论】:

  • 感谢您的回答 :) 我可以,但我正在研究 Doctrine2,似乎他们创建了一个巨大的对象图,并且在调用 flush() 时,他们会检查发生了什么变化,并一口气更新所有内容....这就是我想要弄清楚的。
  • @johnnietheblack - 您可以使用 Doctrine 事件,但这需要稍微修改您的对象。如果您对 Doctrine 事件感兴趣,请查看 stackoverflow.com/questions/2059399/…。它适用于 Doctrine 1.2,但在较新版本中原理相同。
  • 再次感谢 - 我不想以任何方式调整用户类,这样我就可以删除“监听器”并且用户不会知道其中的区别。另外,我想学习如何在没有 Doctrine 的情况下做到这一点......部分是为了好玩,部分是如果我需要知道它是如何完成的
  • 非常感谢 :) 所以,如果我创建了用户的克隆,并更改了其中一个的电子邮件地址,那么 $user != $userClone,是吗?
  • @johnnietheblack - 从理论上讲,没错。我从来没有在 Doctrine 上尝试过,但在常规课程(非 Doctrine 管理)上确实是这样。
【解决方案2】:

php.net 上有一个完整的站点只处理比较对象:

http://php.net/manual/en/language.oop5.object-comparison.php

也是一本好书:

http://www.tuxradar.com/practicalphp/6/12/0

我会说你应该在复制对象时使用“克隆”方法,然后按照 php.net 文章中的说明进行比较。

另外一种快速的方法是在类中声明一个静态方法,该方法只比较相关的“属性”,比如“用户名”、“密码”、“cookie”……

    class User
    {

    public $username;
    public $password;
    public $cookie;

    # other code

    public static function compare(&$obj1, &$obj2)
    {
    if ($obj1->username != $obj2->username)
        return 0
    if ($obj1->password != $obj2->password)
        return 0
    if ($obj1->cookie != $obj2->cookie)
        return 0
    return 1
    }


    };       

【讨论】:

  • 更新非常好,就像我在问题中所说的那样...我希望用户不必更改任何内容即可进行此比较...
  • 这就是存在静态方法的原因,以便在同一类的对象之间进行交互:) 使用此方法,您还可以通过简单地返回一个关联来快速确定哪个“属性”发生了变化。像 $result["username" => "same", "password" => "same", "cookie" => "different"] 这样的数组
  • 真的!我只是主要不希望我的用户类担心将自己与其他任何东西进行比较,因为它并不真正关心用户。它只应该关注关心用户是否改变的服务......你知道吗?
  • 是的,我明白,我曾经为员工进行时间登记。在那里,我使用这种方法来比较 $time-property,并且每天通过 json 格式的文件将其缓存一次。所以你不必每次都查询员工,你可以简单地输出他工作了多少时间。
  • 第二个链接坏了
【解决方案3】:

这是一段支持私有属性的代码:

$reflection_1 = new \ReflectionClass($object_1);
$reflection_2 = new \ReflectionClass($object_2);

$props_1 = $reflection_1->getProperties();

foreach ($props_1 as $prop_1) {

    $prop_1->setAccessible(true);
    $value_1 = $prop_1->getValue($object_1);

    $prop_2 = $reflection_2->getProperty($prop_1->getName());

    $prop_2->setAccessible(true);
    $value_2 = $prop_2->getValue($object_2);

    if ($value_1 === $value_2) {
        // ...
    } else {
        // ...
    }
}


请注意,如果$object_1 具有$object_2 没有的属性,则上述代码在尝试访问它们时会产生错误。

对于这种情况,您首先需要将$reflection_1->getProperties()$reflection_2->getProperties().

【讨论】:

    【解决方案4】:

    通过反射比较dto类的简单例子

    class InvoiceDetails
    {
        /** @var string */
        public $type;
    
        /** @var string */
        public $contractor;
    
        /** @var string */
        public $invoiceNumber;
    
        /** @var \DateTimeInterface|null */
        public $saleDate;
    
        /** @var \DateTimeInterface|null */
        public $issueDate;
    
        /** @var \DateTimeInterface|null */
        public $dueDate;
    
        /** @var string */
        public $payment;
    
        /** @var string */
        public $status;
    
    
        public function isEqual(InvoiceDetails $details): bool
        {
            $reflection = new \ReflectionClass(self::class);
    
            /** @var \ReflectionProperty $property */
            foreach ($reflection->getProperties() as $property) {
                $name = $property->getName();
                if ($this->$name !== $details->$name) {
                    return false;
                }
            }
    
            return true;
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多