|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[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) {
}
}
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 30 22:00:01 2025 UTC |
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.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" } }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.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>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();