#27336 [Com]: inconsistency of unserializing references

From: Date: Tue, 05 Oct 2004 08:34:59 +0000
Subject: #27336 [Com]: inconsistency of unserializing references
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-67122@lists.php.net to get a copy of this message
ID: 27336 Comment by: jan at bestbytes dot de Reported By: hawcue at yahoo dot com Status: Wont fix Bug Type: Class/Object related Operating System: Windows XP PHP Version: 4.3.4 New Comment: <?php // this example is for php 4 class Node { var $value; var $child; var $parent; var $load; var $__destroyed = false; function Node($value) { $this->value = $value; global $load; $this->load = $load; } function setParent(&$parent) { $this->parent = &$parent; } function setChild(&$child) { $this->child = &$child; } /** * @return void * @desc fake __destroy from a php 4 point of view */ function __destroy() { $props = get_object_vars($this); foreach($props as $propName => $propValue) { unset($propValue); if(is_object($this->$propName)) { if(method_exists($this->$propName, '__destroy') AND $this->$propName->__destroyed !== false) { $this->$propName->__destroy(); } } if(isset($this->$propName)) { unset($this->$propName); } } } } header('Content-type: text/plain'); // small load to check the serialized string $loadSize = 4; // big load to check leaking //$loadSize = 32 * 1024; $load = str_repeat('.', $loadSize); for($i = 0;$i<10;$i++) { echo '-------------------- '.(memory_get_usage()/1024).' -------------------------'.chr(10); $serMe = array(); $serMe['parent'] =& new Node('parent'); $serMe['child'] =& new Node('child'); /* * this will work too !! * $serMe['parent'] = new Node('parent'); * $serMe['child'] = new Node('child'); */ $serMe['parent']->setChild($serMe['child']); $serMe['child']->setParent($serMe['parent']); $str = serialize($serMe); /* * 1.Problem: * * You have 2 or more objects with circular references to one another. * Serializing these objects will result in unexpected behaviour when * unserializing them: * * Former references will become copies. * * This is because the php serialize handler can not handle references, * unless all involved objects, variables ... are within an array(), * that will be serialized. * * This should be selfexplaining (just take a look at the length ...): * echo serialize($serMe['parent']); * echo serialize($serMe); * results in: * O:4:"node":5:{s:5:"value";s:6:"parent";s:5:"child";O:4:"node":5:{s:5:"value";s:5:"child";s:5:"child";N;s:6:"parent";O:4:"node":5:{s:5:"value";s:6:"parent";s:5:"child";R:3;s:6:"parent";N;s:4:"load";s:4:"....";s:11:"__destroyed";b:0;}s:4:"load";s:4:"....";s:11:"__destroyed";b:0;}s:6:"parent";N;s:4:"load";s:4:"....";s:11:"__destroyed";b:0;} * a:2:{s:6:"parent";O:4:"node":5:{s:5:"value";s:6:"parent";s:5:"child";O:4:"node":5:{s:5:"value";s:5:"child";s:5:"child";N;s:6:"parent";R:2;s:4:"load";s:4:"....";s:11:"__destroyed";b:0;}s:6:"parent";N;s:4:"load";s:4:"....";s:11:"__destroyed";b:0;}s:5:"child";R:4;} * * PLEASE GIVE THE WORLD PROPER DOCUMENTATION ON THIS (or prove us wrong) * * 2.Problem: * * The loop below will result in extensive leaking (use the big $loadsize ...), * unless you help yourself and unset all the properties manually * see Node::__destroy() * */ $serMe['parent']->__destroy(); $serMe['child']->__destroy(); $serMe = unserialize($str); unset($str); $serMe['parent']->value = 'new parent'; echo '-->'.$serMe['parent']->child->parent->value.chr(10); echo '-->'.$serMe['parent']->value.chr(10); $serMe['parent']->__destroy(); $serMe['child']->__destroy(); unset($serMe); } ?> Previous Comments: ------------------------------------------------------------------------ [2004-02-21 11:55:54] hawcue at yahoo dot com I believe this is a bug. It is related with how PHP determines recursion levels. From what we have observed, I think PHP determines the recursion level by comparing the physical addresses of two references. This is why serialize(&$parentNode) works as expected because the address is passed in. ------------------------------------------------------------------------ [2004-02-21 03:12:20] sniper@php.net Don't mess with references and serializing. That doesn't work and it won't work. ------------------------------------------------------------------------ [2004-02-20 16:22:53] hawcue at yahoo dot com Description: ------------ Please read the codes first. The actual output shows two copies of $parentNode are generated by unserialize(). However, this is not what I expect because I am using reference and $newParent->child->parent and $newParent should refer to the same storage if serialize/unserialize works correctly. The problem can be partially solved by using the following line to do serialize: $str = serialize(&$parentNode); // an ampersand is used This will generate a compiling warning (depreciation of using ampersand in function parameters). Reproduce code: --------------- class Node { var $value; var $child; var $parent; function Node($value) { $this->value = $value; } function setParent(&$parent) { $this->parent = &$parent; } function setChild(&$child) { $this->child = &$child; } } $parentNode = new Node('parent'); $childNode = new Node('child'); $parentNode->setChild($childNode); $childNode->setParent($parentNode); $str = serialize($parentNode); $newParent = unserialize($str); $newParent->value = 'new parent'; echo $newParent->child->parent->value; echo $newParent->value; Expected result: ---------------- new parent new parent Actual result: -------------- parent new parent ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=27336&edit=1

« previous php.bugs (#67122) next »