Hi all,
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, so
figured I'd post for the next person who gets the same problem (y'all might
find it interesting too)...
********************************* 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 *********************************
<?php
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 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 ampersand's 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 ?
--
James Sleeman
Gogo:Code, http://www.gogo.co.nz/
PHP & Coldfusion Programming Services
Email domain : gogo.co.nz see user in from header!