Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if an object has changed

Tags:

php

Is there a more native way (e.x. 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. And some classes may not be cloned.

Doesn't PHP track changes at object properties? And does PHP not expose a method to make use of it?

like image 373
TiMESPLiNTER Avatar asked Feb 24 '15 08:02

TiMESPLiNTER


2 Answers

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.

like image 79
Jacek Kowalewski Avatar answered Oct 18 '22 17:10

Jacek Kowalewski


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

like image 21
Beracah Avatar answered Oct 18 '22 17:10

Beracah