php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #27336 inconsistency of unserializing references
Submitted: 2004-02-20 16:22 UTC Modified: 2004-02-21 11:55 UTC
Votes:3
Avg. Score:5.0 ± 0.0
Reproduced:3 of 3 (100.0%)
Same Version:1 (33.3%)
Same OS:1 (33.3%)
From: hawcue at yahoo dot com Assigned:
Status: Wont fix Package: Class/Object related
PHP Version: 4.3.4 OS: Windows XP
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: hawcue at yahoo dot com
New email:
PHP Version: OS:

 

 [2004-02-20 16:22 UTC] 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

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-02-21 03:12 UTC] sniper@php.net
Don't mess with references and serializing. That doesn't work and it won't work.

 [2004-02-21 11:55 UTC] 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-10-05 10:34 UTC] jan at bestbytes dot de
<?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);
}

?>
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Sun Jan 05 05:01:28 2025 UTC