I\'ve been looking into substantiating a new class instance from a string in PHP. This is seems to be an acceptable procedure, however I am curious why it can\'t be done with th
As stated in the PHP Manual the new
keyword accepts a string or an object as a class name. Optionally you can provide arguments to the constructor in parentheses at the end.
What this means is that the syntax roughly expects:
new [string|object] ()
// ^^ keyword ^^ name ^^ optional parentheses
Another important fact is that the keyword new
has the highest precedence of all operators.
Combining the two facts together means that we cannot use parentheses to increase the precedence of the operation with new
keyword. If we use parentheses PHP will treat them as the optional part of new
syntax. Something like this won't work.
$bar = new ($foo->callBar());
// ^ missing class name
There is just no unambiguous way of telling PHP parser to treat this otherwise.
There is also another caveat worth remembering, which Edward Surov has partially mentioned in his answer. The class name can come from any variable which is a string or an instance.
The following code will create an object of class Bar
:
$obj = ['a'=>'Bar'];
$bar = new $obj['a'];
// or
$obj = (object) ['a'=>'Bar'];
$bar = new $obj->a;
So, let's explain what your code does.
new $foo->callBar ()
// ^^ keyword ^^ name ^^ optional parentheses
Because your Foo
class doesn't have a property callBar
it will trigger Notice message, but PHP will check if it can be used as a class name anyway. And because it doesn't exist it can't be a string or an instance, which is why you see the error.
This has been fixed in PHP 8.
The Variable Syntax Tweaks RFC addressed this issue. Now you can use expressions when creating an instance of a class. The only thing to pay attention to is the operator precedence. When calling function/method you should use ()
to denote precedence. For example:
$bar = new ( $foo->callBar() ) ();
// ^^ expression ^^ optional parentheses
There are exactly four options of creating a class instance using new
that I know:
$bar = new Bar;
$barName = 'Bar'; $bar = new $barName;
$obj = (object) ['barName' => 'Bar']; $bar = new $obj->barName
;$bar1 = new Bar; $bar2 = new $bar1;
First half of an answer to your question is PHP parser: it prohibits many things like new (Foo)
and new "Foo"
that could used to build a hack.
Second half may hide in PHP sources: here's the C function that throws that exception.