Gogo:Code
Providing PHP Programming Services to Design Professionals

Interested in Electronics?

Check out James' Embedded AVR/Arduino Development, and New Zealand Electronic Component Store

Caveat Emptor, this was written before PHP 5 was available, I havn't tested to see if the issue remains or not.

Serialize and Unserialize with Recursive Objects 

I just spent an hour rifling through code to find the cause of a problem only to find it's an oddity with serialization and recursive objects.

Symptoms

When unserializing a previously serialized object, the __wakeup() function is mysteriously called more than once, and the accessing the objects properties from the "outside" gives different results than accessing them from an object contained within the object and holding a recursive reference to the object.

Example


class foo2 {
var $foo = NULL;
function foo2(&$foo) {
$this->foo =& $foo;
}

function fooName() {
echo 'foo2 thinks foo->name is ' . $this->foo->name . '<br>';
}
}

class foo {
var $name = 'unnamed';
var $foo2 = NULL;

function foo() {
$this->foo2 =& new foo2($this);
}

function fooName() {
echo 'foo thinks foo->name is ' . $this->name . '<br>';
}

function __wakeup() {
echo "foo is waking up<br>";
}
}

$myFoo =& new foo();
$myFoo->name = 'myFoo';
echo "I think foo->name is " . $myFoo->name . ".<br>";
$myFoo->fooName();
$myFoo->foo2->fooName();

$serializedFoo = serialize($myFoo);
$myNewFoo = unserialize($serializedFoo);

$myNewFoo->name = 'myNewFoo';
echo "I think foo->name is " . $myNewFoo->name . ".<br>";
$myNewFoo->fooName();
$myNewFoo->foo2->fooName();

Analysis

Execution of the example code above produces...


I think foo->name is myFoo.
foo thinks foo->name is myFoo
foo2 thinks foo->name is myFoo
foo is waking up
foo is waking up
I think foo->name is myNewFoo.
foo thinks foo->name is myNewFoo
foo2 thinks foo->name is myFoo

We can see that the first three lines are as expected, however we see __wakeup is happening twice, and then the final line is in error as foo2 apparently does not see the changed name.

What is happening is that there are actually two seperate foo objects being deserialized, the "top level one" and the one that was originally referenced in foo2, that is, the reference is not being serialized as a reference but rather a whole object. This is evident in the two __wakeup() calls, there are two foo objects being woken up.

Solution

The solution is very simple, when serializing it is necessary to force the passing of a reference to the top-level object being serialized thus..

$serializedFoo = serialize(&$myFoo);

with that form of the serialization (note the ampersand to force passing of reference), the example produces the following..


I think foo->name is myFoo.
foo thinks foo->name is myFoo
foo2 thinks foo->name is myFoo
foo is waking up
I think foo->name is myNewFoo.
foo thinks foo->name is myNewFoo
foo2 thinks foo->name is myNewFoo

We note now there is only one __wakeup() call being made and that all parties agree on the name property of the foo object.

Notes

I was of the belief that the use of ampersands in the arguments to functions was deprecated in favour of ampersands in the function's parameter list declaration (i.e instead of blah(&$my_thing) we declare blah as function blah(&$your_thing) {...}), however this required use of serialize seems to contravene this deprecation as it is plainly necessary to use the ampersand in the function call here. Perhaps this is a hold-over from earlier times and will be changed in the future ?