On the strange behaviour of serialize & unserialize with recursive objects.

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • James Sleeman

    On the strange behaviour of serialize & unserialize with recursive objects.

    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($myFo o);
    $myNewFoo = unserialize($se rializedFoo);

    $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(&$myF oo);

    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_thin g)" we declare blah as "function
    blah(&$your_thi ng) {...}"), 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!
Working...