# RFC: new MyClass()->method() without parentheses

- Status: Implemented (PHP 8.4)
- Authors: Valentin Udaltsov (udaltsov.valentin@gmail.com)
- Date: 2023-12-29
- Target: PHP 8.4
- Implementation: https://github.com/php/php-src/pull/13029
- Original: https://wiki.php.net/rfc/new_without_parentheses
- Page: https://externals.io/rfc/new_without_parentheses
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### Allow to omit parentheses around the new expression as described? (closed)

Yes: 25 · No: 4

Voters: alcaeus (Yes), alec (No), asgrim (Yes), ashnazg (Yes), beberlei (Yes), bukka (Yes), bwoebi (Yes), crell (Yes), derick (Yes), dharman (Yes), galvao (No), ilutov (Yes), jimw (Yes), kalle (No), kguest (Yes), mbeccati (Yes), nicolasgrekas (Yes), ocramius (Yes), petk (Yes), ralphschindler (Yes), ramsey (Yes), santiagolizardo (Yes), sergey (Yes), thekid (Yes), theodorejb (Yes), timwolla (No), trowski (Yes), weierophinney (Yes), wyrihaximus (Yes)

## Discussions on the mailing list

- [Allow (...)->foo() expressions not only for `new`](https://externals.io/message/66197.md) — started on 2013-02-25, 32 messages
- [Suggested change: change priority of new and ->](https://externals.io/message/101811.md) — started on 2018-02-08, 8 messages
- [Raising the precedence of the new operator](https://externals.io/message/113953.md) — started on 2021-04-05, 12 messages
- [\[RFC\] \[Discussion\] new MyClass()->method() without parentheses](https://externals.io/message/123031.md) — started on 2024-04-08, 14 messages
- [\[RFC\]\[Vote\] new MyClass()->method() without parentheses](https://externals.io/message/123293.md) — started on 2024-05-09, 3 messages

---

## Introduction

The “class member access on instantiation” feature [was introduced in PHP 5.4.0](https://php-legacy-docs.zend.com/manual/php5/en/migration54.new-features#:~:text=Class%20member%20access%20on%20instantiation%20has%20been%20added%2C%20e.g.%20(new%20Foo)%2D%3Ebar().). 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:

```php
class Request implements Psr\Http\Message\RequestInterface
{
    // ...
}
 
// OK
$request = (new Request())->withMethod('GET')->withUri('/hello-world');
 
// PHP Parse error: syntax error, unexpected token "->"
$request = new Request()->withMethod('GET')->withUri('/hello-world');
```

The goal of this RFC is to enable the second syntax to:

- make coding with PHP more convenient and satisfy many users' requests for this feature (see [externals#66197](https://externals.io/message/66197), [bugs.php#70549](https://bugs.php.net/bug.php?id=70549), [externals#101811](https://externals.io/message/101811), [externals#113953](https://externals.io/message/113953))
- decrease the visual debt in all sorts of builders and configurators ([more than half a million lines of open source PHP code could be simplified](https://github.com/search?q=path%3A%2A.php+language%3APHP+%2F%5C%28%5Cs%2Anew%5Cs%2B%5B%5E%5C%28%5D%2B%5Cs%2A%5C%28%5B%5E%5C%29%5D%2A%5C%29%5Cs%2A%5C%29%5Cs%2A-%3E%2F&type=code&ref=advsearch))
- ease switching between other C-like languages that don't require parentheses ([Java](https://onecompiler.com/java/3zxuee3bm), [C#](https://onecompiler.com/csharp/3zxudhw4s), [TypeScript](https://onecompiler.com/typescript/3zxudfpyy)).

## Proposal

This RFC allows to omit parentheses around the `new` expression **with** constructor arguments' parentheses. Given class

```php
class MyClass extends ArrayObject
{
    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'; }
}
```

one will be able to write

```php
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"
    new MyClass(['value'])[0],      // string(5)  "value"
);
 
$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"
    new $myClass(['value'])[0],      // string(5)  "value"
);
 
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"
    new (trim(' MyClass '))(['value'])[0],      // string(5)  "value"
);
```

This RFC does not change behavior of `new` expressions **without** constructor arguments' parentheses:

```php
new MyClass::CONSTANT; // will continue to throw a syntax error
new $myClass::CONSTANT; // will continue to throw a syntax error
new MyClass::$staticProperty; // will continue to work as `new (MyClass::$staticProperty)`
new $myClass::$staticProperty; // will continue to work as `new ($myClass::$staticProperty)`
new $myObject->property; // will continue to work as `new ($myObject->property)`
new MyArrayConst['class']; // will continue to throw a syntax error
new $myArray['class']; // will continue to work as `new ($myArray['class'])`
```

This RFC allows to omit parentheses around the `new` anonymous class expression regardless of whether constructor arguments' parentheses are present or not:

```php
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'; } }(),
    // string(5) "value"
    new class (['value']) extends ArrayObject {}[0],
);
```

## Why the proposed syntax is unambiguous

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, `new MyClass()` is unambiguously interpreted as an instantiation of class `MyClass` with zero constructor arguments, not an instantiation of `MyClass()` function call result. This is because PHP interprets the first expression after `new` as a class name.

Consider also `MyClass::new()->method()`. It is not interpreted as `MyClass::(new()->method())` or `MyClass::(new())->method()`. It is interpreted as `(MyClass::new())->method()`. It's natural to assume that `MyClass::new()` is the first expression to evaluate. The same is true for `new MyClass()->method()`: expression `new MyClass()` is also the first to evaluate.

Here's how it looks at the [grammar level](https://github.com/php/php-src/blob/948b2bc2110667cfe53cec253893a3b021f22449/Zend/zend_language_parser.y#L1126). The formula for the `new` expression with explicit constructor arguments' parentheses is `T_NEW class_name|new_variable|(expr) ctor_arguments`, where `new_variable` is a variable expression without calls.

```php
new MyClass();
^   ^      ^
|   |      |
|—T_NEW    |—ctor_arguments
    |
    |—class_name
 
 
new $class();
^   ^     ^
|   |     |
|—T_NEW   |—ctor_arguments
    |
    |—new_variable (cannot have calls!)
 
new (trim(' MyClass '))();
^   ^                  ^
|   |                  |
|—T_NEW                |—ctor_arguments
    |
    |—(expr)
```

Once the parser encounters `T_NEW`, it considers whatever comes next a class name, not a part of another expression. This guarantees that the `new` expression with arguments' parentheses can be unambiguously used as a separate expression without any additional parentheses. It also becomes clear why `new_variable` cannot have calls and why arguments' parentheses are crucial for the proposed syntax: parentheses denote the end of the class name and the end of the new expression.

## Other syntax ideas

Some of the ideas expressed during the discussion of this RFC are listed below. They are orthogonal to this proposal and require a separate RFC.

### Allow to omit the new keyword

Some languages like Kotlin allow to instantiate classes via [MyClass() expression](https://onecompiler.com/kotlin/42b54ds4u). Omitting the “new” keyword in PHP is currently not possible, because classes and functions can [have the same name](https://3v4l.org/mfUVp)(in Kotlin [it is not possible](https://onecompiler.com/kotlin/42b579yhu)). So, in order to achieve this in PHP, we should first deprecate declaring functions and classes with the same names.

### MyClass::new(), MyClass::create()

Introducing a dedicated static constructor like `MyClass::new()` or `MyClass::create()` would be a backward compatibility break for already existing static or object methods named `new` or `create` with required parameters and/or return type different from `static`. So this idea also requires some deprecations.

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

## Proposed Voting Choices

This is a simple yes-or-no vote to include this feature. 2/3 majority required to pass.

Voting started on 2024-05-09 and will end on 2024-05-24 00:00 GMT.

**Vote: Allow to omit parentheses around the new expression as described?** — Yes: 25 · No: 4 (closed)

## Implementation

Pull request contains the final implementation and plenty of tests asserting the expected behavior and backward compatibility: <https://github.com/php/php-src/pull/13029>

## References

- [This RFC discussion thread](https://externals.io/message/123031)

Old requests for this feature:

- [Allow (...)-&gt;foo() expressions not only for &#96;new&#96; (see end of the page)](https://externals.io/message/66197)
- [Allow &#96;new Foo()-&gt;bar()&#96; without parens around &#96;(new Foo)&#96;](https://bugs.php.net/bug.php?id=70549)
- [Suggested change: change priority of new and -&gt;](https://externals.io/message/101811)
- [Raising the precedence of the new operator](https://externals.io/message/113953)
