检查对象是否已更改

时间:2015-02-24 08:36:56

标签: php

是否有更本地的方式(e.x.内置函数),用户土地代码更少,以检查对象属性值是否已更改,而不是使用其中一种方法:

序列化方法

$obj = new stdClass(); // May be an instance of any class

echo $hashOld = md5(serialize($obj)) . PHP_EOL;

$obj->change = true;

echo $hashNew = md5(serialize($obj)) . PHP_EOL;

echo 'Changed: '; var_dump($hashOld !== $hashNew);

结果是:

f7827bf44040a444ac855cd67adfb502 (initial)
506d1a0d96af3b9920a31ecfaca7fd26 (changed)
Changed: bool(true)

卷影复制方法

$obj = new stdClass();
$shadowObj = clone $obj;

$obj->change = true;

var_dump($shadowObj != $obj);

结果是:

bool(true);

这两种方法都有效。但是与非用户区实现相比,两者都有缺点。第一个需要CPU进行序列化和散列,第二个需要内存来存储克隆。有些课程可能无法克隆。

PHP不跟踪对象属性的变化吗? PHP没有公开使用它的方法吗?

5 个答案:

答案 0 :(得分:2)

您要做的是什么?

你试图在一些"未知"之后将对象与自身进行比较。检查对象是否已更改的操作。如果这是真的,那么需要注意一些逻辑点。首先,如果您想将对象与自身进行比较,那么您只有两个选项:

  1. 记住整个对象状态(例如哈希,或只是复制整个对象)
  2. 跟踪一段时间内的变化
  3. 没有其他逻辑方法。比较内存分配,真实对象,复制对象,比较哈希,都是第一点。在第2点内跟踪更改,保存对象内部的更改,记住同时操作。

    所以我认为这个问题是备份数据问题。在这种情况下,有很多很多解决方案,但就我而言,它们都没有在PHP内部进行硬编码。为什么呢?


    答案很简单。 PHP家伙遇到了同样的问题:)。因为如果在php内部进行加密,那么php应该运行/使用其中一种机制(1)或(2)。

    在这种情况下,您创建的每个对象以及您创建的每个操作都应该写在某处以记住每个状态/对象/某些内容,并在将来用它们进行比较。

    虽然您需要此解决方案,但几乎~100%个网站都没有。所以在PHP内部硬编码会使~100%网站工作得更慢,你的工作更快;)。


    PHP假设解决方案?

    唯一的解决方案(可能在将来用php构建)我能想到的是制作某种php配置标志:track objects,并且只有当这个标志是true时才运行所有跟踪对象状态的php机制。 但这也意味着巨大的性能差距。因为所有ifs(如果跟踪,如果跟踪,如果跟踪)也是处理器和内存耗时。

    还有一个问题,要比较什么?你需要将对象与同一个对象进行比较,但......几分钟前?几个操作前几天?不...您必须在代码中指向一个位置,然后在代码中指向第二位并比较这两个位置中的对象。 所以假设的自动跟踪是......有点无能为力,因为没有"键"在对象状态的时间数组。我的意思是,即使你有magic_object_comparer函数,它应该是什么样的?

    <?php
    
        function magic_object_comparer() {} // Arguments??
        function magic_object_comparer($object_before, $object_after) {} // you must save object_before somewhere...??
        function magic_object_comparer($object, $miliseconds) {} // How many miliseconds?
        function magic_object_comparer($object, $operations) {} // How many operations? Which operations?
    
    
        magic_comparer_start($object);
        // ... Few operations...
        $boolean = magic_comparer_compare_from start($object);
        // Same as own implementation...
    ?>
    

    可悲的是,你有自己的实施......

    毕竟,我建议为此实现某种自己的机制,记住只在那里使用它,你需要它。因为这种机制肯定会耗费时间和内存。所以仔细想想:

    1. 您要比较哪些对象。为什么?
    2. 当你想比较它们时?
    3. 是否需要比较所有更改?
    4. 保存这些状态变化的最简单方法是什么?
    5. 毕竟,尝试实现它。我知道你有很多PHP知识,所以我很确定你能找到一些东西。 在这个问题和讨论中也有很多评论和可能的想法。


      但毕竟也许我解释了一些原因,没有内置解决方案,为什么将来不应该有... :)。


      更新

      看看这里:http://www.fluffycat.com/PHP-Design-Patterns/。这是关于php模式的一个很好的资源。您应该查看适配器装饰器观察者模式,以获得可能的优雅面向对象解决方案。

答案 1 :(得分:1)

虽然我也在寻找一种非常快/更快的方法,但方法2的变体实际上是我使用的方法。这种方法的优点是它(非常)快(与isset()相比),具体取决于对象大小。每次更改对象时,您都不必记住设置->modified属性。

global $shadowcopy; // just a single copy in this simple example.
$thiscopy = (array) $obj; // don't use clone.
if ($thiscopy !== $shadowcopy) {
// it has been modified
// if you want to know if a field has been added use array_diff_key($thiscopy,$shadowcopy);

}
$shadowcopy = $thiscopy; // if you don't modify thiscopy or shadowcopy, it will be a reference, so an array copy won't be triggered.

这基本上是方法2,但没有克隆。如果您的属性值是另一个对象(vobj),则可能需要克隆(否则两个引用都将指向同一个对象),但是值得注意的是 对象vobj是否是您想要的看看上面的代码是否有所改变。关于克隆的事情是它正在构建第二个对象(类似的性能),但如果你想看看改变了什么值,你就不关心对象本身,只关心值。并且对象的数组转换速度非常快(约为bool的boolean转换速度的2倍)..好吧,直到大型对象。对于低于100 vals的数组,直接数组比较===非常快。

我非常确定存在更快的方法...

答案 2 :(得分:0)

没有内置方法,我很害怕。阴影复制方法是最好的方法。

一种更简单的方法,如果您可以控制该类,则添加modified变量:

$this->modified = false;

当我以任何方式修改对象时,我只需使用

$obj->modified = true;

这样我以后可以查看

if($obj->modified){ // Do Something

检查是否已修改。在将内容保存到数据库之前,请记住unset($obj->modified)

答案 3 :(得分:0)

我可以为您提供该问题的另一种解决方案,实际上,要检测“对象是否已更改”,我们可以使用observer pattern设计原则。对于某些想获得对象更改通知的人来说,这样做可能会更好。

Contracts / ISubject.php

<?php

namespace Contracts;

interface ISubject
{
    public function attach($observer): void;
    public function detach($observer): void;
    public function notify(): void;
}

Contracts / IObserver.php

<?php

namespace Contracts;

interface IObserver
{
    public function update($subject);
}

Subject.php

class Subject implements ISubject
{
    public $state; // That is detector 

    private $observers;

    public function __construct()
    {
        $this->observers = new \SplObjectStorage(); // That is php built in object for testing purpose I use SplObjectStorage() to store attach()'ed objects.
    }

    public function attach($observer): void
    {
        echo "Subject: Attached an observer.\n";
        $this->observers->attach($observer);
    }

    public function detach($observer): void
    {
        $this->observers->detach($observer);
        echo "Subject: Detached an observer.\n";
    }

    public function notify(): void
    {
        echo "Subject: Notifying observers...\n";
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function someYourLogic()
    {
        $this->state = rand(0, 10);
        echo "Subject: My state has just changed to: {$this->state}\n";
        $this->notify();
    }
}

Observer1.php |另外,您可以根据需要拥有任意数量的观察者

class Observer1 implements IObserver
{
    public function update($subject): void
    {
        if ($subject->state < 5) {
            echo "Observer1: Reacted to the event.\n";
        }
    }
}

Clinet.php

$subject = new Subject();

$o1 = new Observer1();
$subject->attach($o1);
$subject->someYourLogic();

答案 4 :(得分:0)

我们可以在没有观察者的情况下实现它。

对于纯 php,如果需要,我们可以使用 $attributes & $original 来检查已修改的内容 check this explanation

$modifiedValues = [];
foreach($obj->attributes as $column=>$value) {
    if(!array_key_exists($column, $obj->original) || $obj->original[$column] != $value) {
        $modifiedValues[$column] = $value;
    }
}
// then check $modifiedValues if it contains values

对于 Laravel 用户,我们可以使用 isDirty() 方法。它的用法:

$user = App\User::first();
$user->isDirty();          //false
$user->name = "Peter";
$user->isDirty();          //true