ID:               39868
 User updated by:  jb at ez dot no
 Reported By:      jb at ez dot no
-Status:           Bogus
+Status:           Open
 Bug Type:         Unknown/Other Function
 Operating System: Kubuntu 6.10
 PHP Version:      5.2.0
 New Comment:

> 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.


Previous Comments:
------------------------------------------------------------------------

[2006-12-19 10:56:41] [EMAIL PROTECTED]

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.

------------------------------------------------------------------------

[2006-12-19 10:38:45] jb at ez dot no

I'm sorry but this is definitely not Bogus, changing back 
to Open.

------------------------------------------------------------------------

[2006-12-19 09:59:37] jb at ez dot no

> You didn't read my reply, did you? 
> "Master::__construct() is called, you don't see the
> output because the
> exception is thrown just before you print it."

I did read it, and I just think you misunderstood my 
explanation.
If you see in Master::__construct() there is no exception 
throwing, hence this constructor cannot throw any 
exceptions. The throwing is done in the 
Child::__construct() (which does not inherit the Master 
class) and so it will not interfere with the constructor 
of the Master class.

I don't see why PHP would call the constructor then 
immediately stop it before it gets to the first line of 
the constructor code.

Did you try to run this through a debugger (GDB) to verify 
that the constructor is actually run?

------------------------------------------------------------------------

[2006-12-19 09:32:21] [EMAIL PROTECTED]

>You don't think it is a problem that the destructor (for 
>Master) is called while the constructor (for Master) is 
>never called?

You didn't read my reply, did you? 
"Master::__construct() is called, you don't see the output because the
exception is thrown just before you print it."

------------------------------------------------------------------------

[2006-12-19 08:28:46] jb at ez dot no

You don't think it is a problem that the destructor (for 
Master) is called while the constructor (for Master) is 
never called?

In this example neither the constructor or the destructor 
should be called since an exception is thrown in the Child 
class, ie. the new Master() expression should never be 
processed.

The problem seems to be that PHP actually allocates the 
object for Master and then delays the call to construct 
until the parameters are prepared (which makes sense, 
except the allocation). Then when the exception is thrown 
PHP will cleanup all variables/objects, it then sees the 
allocated object and tries the destructor,

In a good object oriented language you would never call 
the destructor unless the constructor was called and was 
completed 100%.

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/39868

-- 
Edit this bug report at http://bugs.php.net/?id=39868&edit=1

Reply via email to