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] [EMAIL PROTECTED]
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