This is an old revision of the document!
PHP RFC: new MyClass()->method() without parentheses
- Date: 2023-12-29
- Author: Valentin Udaltsov vudaltsov@php.net
- Status: Draft
- First Published at: http://wiki.php.net/rfc/new_without_parentheses
- Implementation: https://github.com/php/php-src/pull/13029
Introduction
The “class member access on instantiation” feature
was introduced in PHP 5.4.0.
Since then constants, properties and methods can be accessed on a newly created instance without an intermediate
variable, but only if the new
expression is wrapped in parentheses:
class MyClass { public function method(): void { echo 'Hello, World!'; } } (new MyClass())->method(); // Hello, World! new MyClass()->method(); // PHP Parse error: syntax error, unexpected token "->"
The goal of this RFC is to enable the second sytax without parentheses to:
- make coding with PHP more convenient,
- lower the learning curve,
- decrease the visual debt,
Ambiguity?
At first glance new MyClass()->method()
expression might seem ambiguous. But according to the same logic
new MyClass()
is also ambiguous: is it new (MyClass())
or new MyClass
? However, such ambiguity
does not exist: new MyClass()
is interpreted as an instantiation of class MyClass
with zero
constructor arguments, not as instantiation of MyClass()
function call result.
Here's how it is solved at the grammar level.
The formula for the new
expression is T_NEW class_name|new_variable|(expr) ctor_arguments
, where new_variable
is a variable expression without calls. Thus PHP offers 3 ways to provide a class:
// class_name new MyClass(); // new_variable new $class(); // (expr) new (trim(' MyClass '))();
This guarantees that the new
expression is unambiguous: once the parser encounters T_NEW
, it considers
whatever comes next a class name. Hence it's safe and unambiguous to further use the new
expression on its
own without parentheses. It's like refactoring (MyClass::new())->method()
to MyClass::new()->method()
,
nobody will ever read it as MyClass::(new())->method()
or MyClass::(new()->method())
.
Proposal
This RFC allows to omit parentheses around new
expressions with constructor arguments' parentheses:
class MyClass { const CONSTANT = 'constant'; public static $staticProperty = 'staticProperty'; public static function staticMethod(): string { return 'staticMethod'; } public $property = 'property'; public function method(): string { return 'method'; } public function __invoke(): string { return '__invoke'; } }
var_dump( new MyClass()::CONSTANT, // string(8) "constant" new MyClass()::$staticProperty, // string(14) "staticProperty" new MyClass()::staticMethod(), // string(12) "staticMethod" new MyClass()->property, // string(8) "property" new MyClass()->method(), // string(6) "method" new MyClass()(), // string(8) "__invoke" ); $myClass = MyClass::class; var_dump( new $myClass()::CONSTANT, // string(8) "constant" new $myClass()::$staticProperty, // string(14) "staticProperty" new $myClass()::staticMethod(), // string(12) "staticMethod" new $myClass()->property, // string(8) "property" new $myClass()->method(), // string(6) "method" new $myClass()(), // string(8) "__invoke" ); var_dump( new (trim(' MyClass '))()::CONSTANT, // string(8) "constant" new (trim(' MyClass '))()::$staticProperty, // string(14) "staticProperty" new (trim(' MyClass '))()::staticMethod(), // string(12) "staticMethod" new (trim(' MyClass '))()->property, // string(8) "property" new (trim(' MyClass '))()->method(), // string(6) "method" new (trim(' MyClass '))()(), // string(8) "__invoke" );
This RFC preserves grammar for new
expressions without constructor arguments' parentheses, because in some cases
omitting parentheses around the new
expression will result in a valid expression with different meaning:
(new MyClass)::$staticProperty; // Access static property on MyClass instance new MyClass::$staticProperty; // Instantiate class MyClass::$staticProperty (new $myClass)::$staticProperty; // Access static property on an instance of class $myClass new $myClass::$staticProperty; // Instantiate class $myClass::$staticProperty (new $myClass)->property; // Access non-static property on an instance of class $myClass new $myClass->property; // Instantiate class $myClass->property (new $myClass)->method(); // Call method on an instance of class $myClass new $myClass->method(); // Instantiate class $myClass->method
This RFC allows to omit parentheses around any new
anonymous class expressions regardless of constructor arguments'
parentheses:
var_dump( // string(8) "constant" new class { const CONSTANT = 'constant'; }::CONSTANT, // string(14) "staticProperty" new class { public static $staticProperty = 'staticProperty'; }::$staticProperty, // string(12) "staticMethod" new class { public static function staticMethod() { return 'staticMethod'; } }::staticMethod(), // string(8) "property" new class { public $property = 'property'; }->property, // string(6) "method" new class { public function method() { return 'method'; } }->method(), // string(8) "__invoke" new class { public function __invoke() { return '__invoke'; } }(), );
Backward Incompatible Changes
None. Any code that is valid before the change will be valid after and interpreted in the same way.
Proposed PHP Version(s)
PHP 8.4