php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54170 Object instantion argument not executed when the constructor is missing
Submitted: 2011-03-05 19:21 UTC Modified: 2013-10-22 12:06 UTC
Votes:3
Avg. Score:4.3 ± 0.9
Reproduced:3 of 3 (100.0%)
Same Version:3 (100.0%)
Same OS:1 (33.3%)
From: martijn at site-to-make dot nl Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.3.5 OS: Debian 6.0
Private report: No CVE-ID: None
 [2011-03-05 19:21 UTC] martijn at site-to-make dot nl
Description:
------------
I runned in a very weird issue. When i create a new object, with another object instantation as argument, the 2nd object is not created if the first class has no constructor. It looks the arguments are simply not executed when the constructor is missing. When a constructor is added, it works like it should.

I tested this with multiple 5.3 versions and its confirmed as not working by multiple people.

Test script:
---------------
<?php

class A {}

new A(new ThisIsANotExistingClass());


Expected result:
----------------
A nice fatal error since ThisIsANotExistingClass is missing.

Actual result:
--------------
The scripts runs fine and nothing apears on the screen.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-03-05 19:33 UTC] info at liefcoden dot nl
As an addition (because it is probably the same problem):

class B {
  public function __construct( ) {
    echo 'B::__construct();';
  }
}

class A { }

new A( new B( ) );

The above code prints nothing as well.
 [2011-03-05 19:41 UTC] rasmus@php.net
-Status: Open +Status: Bogus
 [2011-03-05 19:41 UTC] rasmus@php.net
Without a constructor the arguments to the new call mean nothing and are 
optimized away. The object is still created:

class A {  }
$b = new A(new ThisIsANotExistingClass());
var_dump($b);

outputs:

object(A)#1 (0) {
}
 [2011-03-05 19:47 UTC] info at liefcoden dot nl
Well then, let me tell my customers their applications might break down because 
of this "feature". Thanks very much.
 [2013-09-28 15:05 UTC] MonYeaTea at ist dot mit dot edu
Well, the problem is that PHP does not break down here.

It's okay to optimize here, and as Rasmus named it first that there is 
an evaluation optimization with the new operator, let's start to name 
that optimization after him: Rasmus evaluation.

This Rasmus evaluation is also the case with any other non-existent 
function or method. The arguments to these aren't evaluated either.

However - and this seems to be missing here - these function/method 
calls are producing fatal errors - the missing constructor function 
doesn't.

The workaround (in your case) is to not use the new operator but to 
use ReflectionClass::newInstance():


<?php

class A {}

(new ReflectionClass('A'))->newInstance(new ThisIsANotExistingClass());


However there is a caveat: With this workaround you do not profit from 
the Rasmus evaluation any longer. The fatal error will be thrown *after* 
the arguments have been evaluated. This is because of the layer of 
indirection the ReflectionClass adds here.

Hope this helps until PHP is fixed and throws a fatal error when a 
non-existent constructor is called in the future.

Then you can profit safely from Rasmus evaluation again.
 [2013-10-22 12:06 UTC] bwoebi@php.net
Related to #65930
 [2013-10-31 20:18 UTC] php at mlemoine dot name
Well, the problem is not occurring only if the arguments of constructor are supposed to be throwing a Fatal Error.

I think the problem is even bigger if the arguments of the constructor are valid and expected to provide some kind of side-effect.

If the class name is provided via a variable, you end up with a very nasty corner case where your side-effects are sometimes executed, and sometimes not.

Please refer to https://bugs.php.net/bug.php?id=65930 (Marked as Duplicate of this bug, but in my opinion with a much higher severity).

This is not okay to optimize this away. With PHP the only other case where the arguments of a function call are not evaluated is when the function does not exists and then a Fatal Error is thrown.

This doesn't matter much since it won't have any effect unless the side-effect were supposed to have an impact outside of PHP (e.g. creating a file), but in any case, this could be seen as "You're function is resolved first, then the arguments are evaluated, then the function is called with the computed value of the arguments. Since the function doesn't exist, an error is thrown and your arguments never get evaluated.". Dealing with unresolved side-effects in this case is part of error management, but at least, you have an active and immediate way of telling that something could have gone wrong.

In the case of a non-existing constructor, your side effect are not resolved, but unless you go digging within the Reflection API, nothing, not even a strict or a notice is going to tell that something could have gone wrong regarding your side-effects and your object is still going to be built as nothing wrong happened. This is a silent fail which is definitely WRONG and a recipe for debugging headache.

In this case, I think the best way to go would be to replace the "no constructor but a weird behaviour" implementation by a "default constructor with an empty body if none is defined or inherited with no "Rasmus unoptimization". This is much saner and the least surprising behavior.

In the mean time, the right behavior is not to use the Reflection API, but to make sure the arguments evaluation would have no side-effects. In this case:

$tmp_arg_1 = new ThisIsANotExistingClass();
new A($tmp_arg_1);
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 26 15:01:56 2024 UTC