note 34534 deleted from function.serialize by aharvey

From: Date: Tue, 06 Nov 2012 02:02:55 +0000
Subject: note 34534 deleted from function.serialize by aharvey
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-191739@lists.php.net to get a copy of this message
Note Submitter: james at gogo dot co dot nz ---- 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. ********************************* 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 (http://www.gogo.co.nz/)

« previous php.notes (#191739) next »