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

Reply via email to