简体   繁体   English

检查对象是否已更改

[英]Check if an object has changed

Is there a more native way (ex a built-in function) with less userland code to check if an objects property values have changed instead of using one of those methods:是否有一种更原生的方式(例如内置函数),使用更少的用户空间代码来检查对象属性值是否已更改,而不是使用以下方法之一:

The serialize approach序列化方法

$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);

Which results in:结果是:

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

The shadow copy approach影子复制方法

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

$obj->change = true;

var_dump($shadowObj != $obj);

Which results in:结果是:

bool(true);

Both approaches work.这两种方法都有效。 But both have disadvantages compared to a non userland implementation.但是与非用户空间实现相比,两者都有缺点。 The first one needs CPU for serialization and hashing and the second one needs memory for storing clones.第一个需要 CPU 进行序列化和散列,第二个需要内存来存储克隆。 And some classes may not be cloned.并且某些类可能无法克隆。

Doesn't PHP track changes at object properties? PHP 不跟踪对象属性的变化吗? And does PHP not expose a method to make use of it? PHP 没有公开使用它的方法吗?

What you are trying to do?你想做什么?

You are trying to compare object with itself, after some chain of "unknown" operations to check if the object has changed.您正在尝试将对象与自身进行比较,经过一些“未知”操作链以检查对象是否已更改。 If this is true, there are some logical points to observe.如果这是真的,有一些逻辑点需要观察。 At first, if you want to compare object with itself, you've got only two options:首先,如果您想将对象与其自身进行比较,您只有两个选择:

  1. Remember the whole object state (for example hash, or just copy whole object)记住整个对象状态(例如哈希,或者只是复制整个对象)
  2. Track changes over time跟踪随时间的变化

There is no other logical approach.没有其他合乎逻辑的方法。 Comparing memory allocations, real objects, copying objects, comparing hashes, is all in point one.比较内存分配、真实对象、复制对象、比较哈希,都是第一点。 Tracking changes, saving changes inside object, remembering meantime operations, inside point 2.跟踪更改,保存对象内部的更改,记住期间的操作,内部第 2 点。

So in my opinion this question is sort of backing up data questions.所以在我看来,这个问题有点像备份数据问题。 In that case there are many, many solutions but none of them are hardcoded inside php as far as I'm concerned.在这种情况下,有很多解决方案,但就我而言,没有一个是在 php 中硬编码的。 Why?为什么?


The answer is simple.答案很简单。 PHP guys have got the same problems you've got :). PHP 人员也遇到了与您相同的问题:)。 Because if this would be hardocded inside php, then php should run / use one of those mechanisms (1) or (2).因为如果这会在 php 中被硬存储,那么 php 应该运行/使用那些机制 (1) 或 (2) 之一。

In that case every object that you create, and every operation you made should be written somewhere to remember every state / object / something and use them for comparison in the future.在这种情况下,您创建的每个对象以及您所做的每个操作都应该写在某个地方以记住每个状态/对象/事物,并在将来使用它们进行比较。

While you need this solution, almost ~100% of websites don't.虽然您需要此解决方案,但几乎~100%的网站不需要。 So hardcoding this inside php would made ~100% of websites work slower and your work faster ;).因此,在 php 中对此进行硬编码会使~100%的网站运行速度变慢,而您的工作速度变快 ;)。


PHP hypothetical solution? PHP假设解决方案?

The only solution (maybe built in php in the future) I can think of is making some kind of php config flag: track objects , and only if this flag is true , then run all the php mechanisms of tracking objects states.我能想到的唯一解决方案(将来可能会内置在 php 中)是制作某种 php 配置标志: track objects ,并且仅当此标志为true ,才运行跟踪对象状态的所有 php 机制。 But this also mean a huge performance gap.但这也意味着巨大的性能差距。 As all the ifs (if tracking, if tracking, if tracking) are also procesor and memory time consuming.因为所有的ifs (如果跟踪,如果跟踪,如果跟踪)也是处理器和内存耗时。

There is also a problem, what to compare?还有一个问题,比较什么呢? You need to compare object with same object, but... Few minutes ago?您需要将对象与同一对象进行比较,但是...几分钟前? Few operations ago?前几次操作? No... You must point exactly one place in code, and then point second place in code and compare object in those two places.不...您必须在代码中精确指向一个位置,然后在代码中指向第二个位置并在这两个位置比较对象。 So hypothetical auto tracking is... Kind of powerless, as there is no "key" in the object state ofer time array.所以假设的自动跟踪是......有点无能为力,因为在时间数组的对象状态中没有“关键”。 I mean, even if you got magic_object_comparer function, what it should look like?我的意思是,即使你有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...
?>

Sadly, you are left with own implementation...可悲的是,你只剩下自己的实现......

After all, I would propose to implement some kind of own mechanism for that, and remember to use it only there, where you need it.毕竟,我建议为此实现某种自己的机制,并记住只在需要的地方使用它。 As this mechanism will for sure be time and memory consuming.因为这种机制肯定会消耗时间和内存。 So think carefully:所以仔细想想:

  1. Which objects you want to compare.您要比较哪些对象。 Why?为什么?
  2. When you want to compare them?当您想比较它们时?
  3. Does all changes need to be compared?是否需要比较所有更改?
  4. What is the easiest way of saving those states changes?保存这些状态更改的最简单方法是什么?

And after all of that, try to implement it.毕竟,尝试实施它。 I see that you've got a huge php knowledge, so I'm pretty sure that you will figure out something.我看到你已经掌握了大量的 php 知识,所以我很确定你会想出一些东西。 There are also many comments, and possible ideas in this question and discussion.在这个问题和讨论中也有很多评论和可能的想法。


But after all maybe I explained a little why, there is no build in solution, and why there should not be one in the future... :).但毕竟也许我解释了一点为什么,没有内置的解决方案,为什么将来不应该有一个...... :)。


UPDATE更新

Take a look here: http://www.fluffycat.com/PHP-Design-Patterns/ .看看这里: http : //www.fluffycat.com/PHP-Design-Patterns/ This is a great resource about php patterns.这是关于 php 模式的一个很好的资源。 You should take a look at adapter , decorator and observer patterns, for possible elegant object oriented solutions.您应该查看适配器装饰器观察者模式,以获得可能的优雅的面向对象解决方案。

While I too am looking for a very fast/faster approach, a variant of method 2 is effectively what I use.虽然我也在寻找一种非常快速/更快的方法,但我有效地使用了方法 2 的变体。 The advantage of this method is that it is (pretty) fast (in comparison to an isset()), depending on object size.这种方法的优点是它(相当)快(与 isset() 相比),具体取决于对象大小。 And you don't have to remember to set a ->modified property each time you change the object.而且您不必记住每次更改对象时都设置->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.

This is basically method 2, but without the clone.这基本上是方法 2,但没有克隆。 If your property value is another object (vobj), then clone may be necessary (otherwise both references will point to the same object), but then it is worth noting that it is that object vobj you want to see if has changed with the above code.如果你的属性值是另一个对象(vobj),那么可能需要clone(否则两个引用都会指向同一个对象),但是接下来值得注意的是你要查看的那个对象vobj是否随着上面的变化而变化代码。 The thing about clone is that it is constructing a second object (similar performance), but if you want to see what values changed, you don't care about the object itself, only the values.克隆的事情是它正在构造第二个对象(类似的性能),但是如果您想查看更改了哪些值,则不必关心对象本身,只关心值。 And array casting of an object is very fast (~2x the speed of a boolean cast of a bool) .. well, up until large objects.并且对象的数组转换非常快(大约是布尔值转换的速度的 2 倍)……好吧,直到大对象。 Also direct array comparison === is very fast, for arrays under say 100 vals.此外,直接数组比较 === 非常快,对于低于 100 vals 的数组。

I'm pretty sure an even faster method exists...我很确定存在一种更快的方法......

I can offer you another solution to the problem, In fact to detect "if an object has changed" we can use observer pattern design principles.我可以为您提供另一种解决方案,实际上要检测“对象是否已更改”,我们可以使用观察者模式设计原则。 May that way should be better for some people who want to get notify about changes in object.对于一些想要获得对象更改通知的人来说,这种方式可能会更好。

Contracts/ISubject.php合同/ISubject.php

<?php

namespace Contracts;

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

Contracts/IObserver.php合同/IObserver.php

<?php

namespace Contracts;

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

Subject.php主题.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 | Observer1.php | Plus you are able to have as many ConcreteObserver as you want此外,您可以拥有任意数量的 ConcreteObserver

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

Clinet.php客户端.php

$subject = new Subject();

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

There is no built-in method, I'm afraid.恐怕没有内置方法。 The shadow copy approach is the best way.卷影复制方法是最好的方法。

A simpler way, if you have control over the class, is to add a modified variable:如果您可以控制类,则更简单的方法是添加modified变量:

$this->modified = false;

When I modify the object in any way, I simply use当我以任何方式修改对象时,我只需使用

$obj->modified = true;

This way I can later check这样我以后可以检查

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

to check if it was modified.检查它是否被修改。 Just remember to unset($obj->modified) before saving content in a database.请记住在将内容保存到数据库之前unset($obj->modified)

We can implement it without observer.我们可以在没有观察者的情况下实现它。

For pure php, we can use $attributes & $original to check what has been modified check this explanation if needed.对于纯 php,我们可以使用$attributes & $original来检查已修改的内容,如果需要,请查看此说明

$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

For Laravel user, we can use the isDirty() method.对于 Laravel 用户,我们可以使用isDirty()方法。 Its usage:它的用法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM