php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #25975 PHP 5 object references don't survive serialization
Submitted: 2003-10-24 08:08 UTC Modified: 2004-02-02 04:00 UTC
Votes:6
Avg. Score:4.7 ± 0.5
Reproduced:5 of 5 (100.0%)
Same Version:3 (60.0%)
Same OS:4 (80.0%)
From: reiersol at online dot no Assigned:
Status: Closed Package: Scripting Engine problem
PHP Version: 5CVS OS: Linux RedHat 9.0
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: reiersol at online dot no
New email:
PHP Version: OS:

 

 [2003-10-24 08:08 UTC] reiersol at online dot no
Description:
------------
Object references inside PHP5 objects are not preserved through serialize/unserialize like traditional PHP4 references. This means they cannot be used in session-based applications.

Reproduce code:
---------------
class Bar {}
class Foo {
    var $v1;
    var $v2;
    function Foo() {
        $this->v1 = new Bar;
        $this->v2 = $this->v1;
    }
}

$f = new Foo;
var_dump($f);
$g = unserialize(serialize($f));
var_dump($g);
print $s1;


Expected result:
----------------
This is what I get if I use $this->v2 = &this->$v1 instead of $this->v2 = $this->v1:

object(foo)#1 (2) {
  ["v1"]=>
  &object(bar)#2 (0) {
  }
  ["v2"]=>
  &object(bar)#2 (0) {
  }
}
object(foo)#3 (2) {
  ["v1"]=>
  &object(bar)#4 (0) {
  }
  ["v2"]=>
  &object(bar)#4 (0) {
  }
}

Actual result:
--------------
object(foo)#1 (2) {
  ["v1"]=>
  object(bar)#2 (0) {
  }
  ["v2"]=>
  object(bar)#2 (0) {
  }
}
object(foo)#3 (2) {
  ["v1"]=>
  object(bar)#4 (0) {
  }
  ["v2"]=>
  object(bar)#5 (0) {
  }
}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-10-31 03:42 UTC] reiersol at online dot no
I guess I'll have to expand my example:

class Bar { var $value = 0; }
class Foo {
    var $v1;
    var $v2;
    function Foo() {
        $this->v1 = new Bar;
        $this->v2 = $this->v1;
    }
}

$f = new Foo;
$f->v2->value = 42;
var_dump($f);
$g = unserialize(serialize($f));
$g->v2->value = 'and now for something completely different';
var_dump($g);

Here's the output:

object(foo)#1 (2) {
  ["v1"]=>
  object(bar)#2 (1) {
    ["value"]=>
    int(42)
  }
  ["v2"]=>
  object(bar)#2 (1) {
    ["value"]=>
    int(42)
  }
}
object(foo)#3 (2) {
  ["v1"]=>
  object(bar)#4 (1) {
    ["value"]=>
    int(42)
  }
  ["v2"]=>
  object(bar)#5 (1) {
    ["value"]=>
    string(42) "and now for something completely different"
  }
}

That should at least make it clear that there's a difference in behavior before and after serialization. And the behavior before serialization is the behavior of a normal object-oriented language. (I ported the example to Java just to make sure I wasn't crazy.)

I'm not trying to split hairs. I tried creating the kind of sophiticated object-oriented structure that PHP 5 makes so much easier. It worked wonderfully. But then I discovered that the structure didn't persist across sessions. So I made this simplified example to demonstrate the problem.
 [2003-11-04 00:28 UTC] sniper@php.net
Add the missing expected result to your last example.



 [2003-11-04 01:23 UTC] reiersol at online dot no
object(foo)#1 (2) {
  ["v1"]=>
  object(bar)#2 (1) {
    ["value"]=>
    int(42)
  }
  ["v2"]=>
  object(bar)#2 (1) {
    ["value"]=>
    int(42)
  }
}
object(foo)#3 (2) {
  ["v1"]=>
  object(bar)#4 (1) {
    ["value"]=>
    string(42) "and now for something completely different"
  }
  ["v2"]=>
  object(bar)#4 (1) {
    ["value"]=>
    string(42) "and now for something completely different"
  }
}
 [2003-11-09 12:34 UTC] moriyoshi@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5-latest.tar.gz
 
For Windows:
 
  http://snaps.php.net/win32/php5-win32-latest.zip

This should be fixed in CVS now.

 [2003-11-10 05:48 UTC] reiersol at online dot no
Thanks, but I'm afraid this is not quite good enough.

What I'm getting now after serialize/unserialize is:

object(foo)#3 (2) {
  ["v1"]=>
  &object(bar)#4 (1) {
    ["value"]=>
    string(42) "and now for something completely different"
  }
  ["v2"]=>
  &object(bar)#4 (1) {
    ["value"]=>
    string(42) "and now for something completely different"
  }
}

The ampersands occur only after serialization. They indicate that this is what is known as a reference in PHP 4, which is a symbol table alias. The objects still don't behave the same before and after. Try this:

$f->v2 = 'I\'m no longer an object';
$g->v2 = 'I\'m no longer an object';
var_dump($f);
var_dump($g);

As before, $f is the object before serialize/unserialize, $g is the object after. The output is:

object(foo)#1 (2) {
  ["v1"]=>
  object(bar)#2 (1) {
    ["value"]=>
    int(42)
  }
  ["v2"]=>
  string(23) "I'm no longer an object"
}
object(foo)#3 (2) {
  ["v1"]=>
  &string(23) "I'm no longer an object"
  ["v2"]=>
  &string(23) "I'm no longer an object"
}

As I understand it, in $f we are replacing an object reference with a string. In $g, we are replacing the value of a variable that's aliased to another.

You might ask whether this has any practical consequences. I think that sooner or later it will (in fact, it seems to be happening in my full-scale example). When it does, it will be very confusing to the people who encounter the problem. You may get away with this for a while, but the longer you get away with it the more difficult it might be to figure out.
 [2003-11-28 20:40 UTC] sniper@php.net
Please provide an example script that actually uses sessions,
(it's not the same thing when you do the serialize/unserialize inside the same script..)


 [2003-12-04 09:52 UTC] reiersol at online dot no
OK. Here is an example using sessions. Exactly the same thing happens. 

<?php
class Bar { public $value = 0; }
class Foo {
    public $v1;
    public $v2;
    function Foo() {
        $this->v1 = new Bar;
        $this->v2 = $this->v1;
    }
}
session_start();

if (isset($_SESSION['g'])) {
    //Try these two one at a time to see the different behaviors:
//    $_SESSION['g']->v2 = "I'm no longer an object";
    $_SESSION['g']->v2->value = 42;
} else {
    $_SESSION['g'] = new Foo;
}
?>
<pre>
<?php 
var_dump($_SESSION['g']);
?>
</pre>
 [2004-01-11 11:00 UTC] sniper@php.net
Seems to be fixed now.

 [2004-01-19 05:23 UTC] reiersol at online dot no
For the record: I don't think this bug has been fixed, and I think it's a very serious one, but I'm giving up.
 [2004-01-21 20:16 UTC] cunha17 at uol dot com dot br
If it's not fixed, please don't give up !
Serialization/Deserialization should work transparently in the same script or using sessions !!!
Just provide a simple script with the actual and expected results...
 [2004-01-22 03:36 UTC] reiersol at online dot no
In response to cunha17 at uol dot com dot br:

As far as I can tell, nothing has happened since my November 10 submission. The examples I've given already *should* be more than sufficient. Perhaps it would be helpful
if you double-check this. To make it really easy, here's a
PHPUnit test to run. Both tests should pass. In PHP 5 beta 1, the testReplaceObject passes, the other one fails. In the versions after the dubious fix (including today's CVS version), it's reversed.

require_once 'PHPUnit.php';
class Bar { var $value = 0; }
class Foo {
    var $v1;
    var $v2;
    function Foo() {
        $this->v1 = new Bar;
        $this->v2 = $this->v1;
    }
}

class ReferenceTest extends  PHPUnit_TestCase {

    function testChangeObject() {
        $g = unserialize(serialize(new Foo));
        $g->v2->value = 42;
        $this->assertEquals(42,$g->v1->value);
    }
    function testReplaceObject() {
        $g = unserialize(serialize(new Foo));
        $g->v2 = 42;
        $this->assertTrue($g->v1 instanceof Bar);
    }
}

$suite = new PHPUnit_TestSuite();
$suite->addTestSuite('ReferenceTest');
$result = PHPUnit::run($suite);
echo $result->toString();
 [2004-02-01 05:28 UTC] stas@php.net
Works for me too, just as in your "Expected result".
 [2004-02-02 04:00 UTC] reiersol at online dot no
OK, I'm beginning to think I understand what the problem is. You're testing my "expected result" from before the attempted bugfix in early November. That doesn't work.

Since the history of the bug seems to be causing confusion, I've now reported the "fixed" version as a new bug:

http://bugs.php.net/27120
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 04:01:27 2024 UTC