ID: 38018
User updated by: php dot bugs at login dot hedim dot ch
Reported By: php dot bugs at login dot hedim dot ch
-Status: Bogus
+Status: Open
Bug Type: Class/Object related
-Operating System: FreeBSD
+Operating System: irrelevant
PHP Version: 5.1.4
New Comment:
Thank you for your response.
While I admit that the code example I gave was confusing, I believe it
is not bogus. It is true that there is only one call to __destruct with
the changed code, but it's the wrong one. The correct call to __destruct
has disappeared for a different reason.
This is what happens with the changed code: Because the Foo instance #1
is assigned to $var, it would only be destructed when $var goes out of
scope or (as in this case) when the script terminates (normally).
When unserializing $str, __wakeup is called on the unserialized Foo
instance #2 and throws an exception. Then, __destruct is called on Foo
instance #2. This is imho wrong because Foo instance #2 doesn't really
exist. The exception indicates it could not be reconstructed, just like
an exception from __construct does. If Foo instance #2 doesn't really
exists, there is nothing to destruct and __destruct should not be
called.
The correct call to __destruct on Foo instance #1 doesn't happen
anymore because the above exception terminates the script with a fatal
error. Wrapping a try { } around the unserialize would bring the call
to Foo #1's __destruct back.
To prove this, I have extended the reproduce code (see below) to also
print out which object is destructed.
The point is this: If __destruct is not called for objects that throw
an exception from __construct, then it should not be called either for
those that throw an exception from __wakeup. In both cases, the object
"could not be (re-) constructed", therefore there is nothing to
destruct.
I hope this makes things clearer and I apologize for the confusing
description and code example before.
Thank you for looking into this matter again.
Extended reproduce code:
------------------------
class Foo
{
function __wakeup()
{
throw new Exception;
}
function __destruct()
{
echo "hello from $this's destructor\n";
}
}
$var = new Foo;
echo "$var\n";
$str = serialize($var);
unserialize($str);
Expected result:
----------------
Object id #1
Fatal error: Uncaught exception 'Exception' [...]
No output from any destructor. The exception terminates the script
before any object can be destructed.
Actual result:
--------------
Object id #1
hello from Object id #2's destructor
Fatal error: Uncaught exception 'Exception' [...]
Previous Comments:
------------------------------------------------------------------------
[2006-07-05 21:36:24] [EMAIL PROTECTED]
Change your code to this:
$var = new Foo;
$str = serialize($var);
unserialize($str);
and voila - no second destructor call.
The first destructor is called for the temporary var.
------------------------------------------------------------------------
[2006-07-05 20:31:32] php dot bugs at login dot hedim dot ch
Description:
------------
If __wakeup threw an exception when unserializing an object, __destruct
is called on that object. For the same reasons as __destruct is not
called if __construct threw an exception (see bug #29368), it should
not be called here either.
Reproduce code:
---------------
class Foo
{
function __wakeup()
{
throw new Exception;
}
function __destruct()
{
echo "hello from destructor\n";
}
}
unserialize(serialize(new Foo));
Expected result:
----------------
hello from destructor (from the destructor of the object to be
serialized)
Actual result:
--------------
hello from destructor (from the destructor of the object to be
serialized)
hello from destructor (from the destructor of the object that fails to
unserialize)
------------------------------------------------------------------------
--
Edit this bug report at http://bugs.php.net/?id=38018&edit=1