|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2006-12-18 14:31 UTC] jb at ez dot no
Description:
------------
It is possible for PHP to call the __destruct() on an
object allthough __construct() was never called. This
seems to happen when having nested object creations and
the inner one throws and exception in the constructor.
The attached reproducable code shows the problem:
Child::__construct() [Child]
Master::__destruct() [Master]
If the last line is changed to:
$b = new Child(); $a = new Master( $b );
it works of course.
In general destructors should never be called if the
constructor was not called or even if it did not finish.
Reproduce code:
---------------
class Master {
public function __construct( $child ) {
echo __CLASS__, "::__construct() [", get_class( $this ), "]\n";
}
public function __destruct() {
echo __CLASS__, "::__destruct() [", get_class( $this ), "]\n";
}
}
class Child {
public function __construct() {
echo __CLASS__, "::__construct() [", get_class( $this ), "]\n";
throw new Exception( "Child failure" );
}
public function __destruct() {
echo __CLASS__, "::__destruct() [", get_class( $this ), "]\n";
}
}
$a = new Master( new Child() );
Expected result:
----------------
Child::__construct() [Child]
Actual result:
--------------
Child::__construct() [Child]
Master::__destruct() [Master]
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Nov 07 08:00:02 2025 UTC |
> Your arguments are based on wrong assumption that object > is created in > __construct(). This is wrong, because __construct() is > supposed to > initialize it, the object itself is created by "new" and > already exists > in __construct(). > No bug here. I'm very aware that the constructor does not create/allocate the object and that the constructor is used for initialization. And I never claimed that it did so. What I'm saying is that the constructor (ie. initialization) is never called (which is correct since there is an exception) and therefore the destructor (which is uninitialization) should not be called. ie. this expression: $a = new Master( new Child() ); would become this (using fake opcodes). 1. allocate instance of Master as $0 2. allocate instance of Child as $1 3. call $1->__construct() 4. call $0->__construct( $1 ); the end of the script would then do: 5. if not initialized $1 goto 7 6. call $1->__destruct() 7. if not initialized $0 goto 9 8. call $0->__destruct() 9. free instance $1 10. free instance $0 Since line #3 gives an exception it will never do #4, however the script ends and it performs the freeing of the objects but not the destructor of Master since it never called __construct. An alternative would also be: 1. allocate instance of Child as $1 2. call $1->__construct() 3. allocate instance of Master as $0 4. call $0->__construct( $1 ); To avoid the Master object being allocated at all. Whichever way is chosen the __destruct() should not be called in any case. If PHP is meant to work this way there is something seriously wrong with its design and should be fixed. And also to demonstrate the problem with a real-life example, consider this PHP code: class Master { public function __construct( $child ) { echo __CLASS__, "::__construct() [", get_class( $this ), "]\n"; $this->child = $child; } public function __destruct() { echo __CLASS__, "::__destruct() [", get_class( $this ), "]\n"; $this->child->callByMaster(); } } class Child { public function __construct() { echo __CLASS__, "::__construct() [", get_class( $this ), "]\n"; throw new Exception( "Child failure" ); } public function __destruct() { echo __CLASS__, "::__destruct() [", get_class( $this ), "]\n"; } public function callByMaster() { echo __CLASS__, "::__callByMaster() is called [", get_class( $this ), "]\n"; } } $a = new Master( new Child() ); You will now get a fatal error in the destruct: Child::__construct() [Child] Master::__destruct() [Master] Notice: Undefined property: Master::$child in construct2.php on line 9 Call Stack: 0.0003 1. {main}() construct2.php:0 0.0005 2. Master->__destruct() construct2.php:0 Fatal error: Call to a member function callByMaster() on a non-object in construct2.php on line 9 Call Stack: 0.0003 1. {main}() construct2.php:0 0.0005 2. Master->__destruct() construct2.php:0 Now the only way to protect against this problem is to use a boolean to set if __construct() was called. However that means that programmer must take care of issues which PHP itself should solve for him.