Check if an object has changed

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.

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


The answer is simple. PHP guys have got the same problems you've got :). Because if this would be hardocded inside php, then php should run / use one of those mechanisms (1) or (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. So hardcoding this inside php would made ~100% of websites work slower and your work faster ;).


PHP hypothetical solution?

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

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?

<?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. 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/. This is a great resource about php patterns. 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. The advantage of this method is that it is (pretty) fast (in comparison to an isset()), depending on object size. And you don't have to remember to set a ->modified property each time you change the object.

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. 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. 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. Also direct array comparison === is very fast, for arrays under say 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

<?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 | Plus you are able to have as many ConcreteObserver as you want

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

Tags:

Php