0

PHP 8.4 — new without parentheses for method chaining

Beginner5 min read·php-09-024

Concept

PHP 8.4 introduced new without parentheses for method chaining. Before PHP 8.4, you couldn't chain methods directly on a new expression without wrapping it in parentheses: (new MyClass())->method(). PHP 8.4 allows new MyClass()->method() without the wrapping parentheses.

Why this matters: It's a quality-of-life improvement for fluent interfaces and method chaining. The extra parentheses around new expressions in chains were a visual tax that didn't add any semantic value.

Affected contexts: Works with new ClassName(), new ClassName, and new $variable(). Also works with property access (new Obj()->prop), array access (new Obj()['key']), and calling the object as a function (new Invokable()()).

Edge case — constructor args: The object's constructor arguments still require parentheses: new MyClass(1, 2)->method(). The change is that the second set of method-call parentheses no longer requires the whole new expression to be wrapped.

Impact on code style: Many developers already relied on this in static analysis (it was sometimes interpreted correctly) or used the parenthesis workaround. PHP 8.4 makes it official and consistent.

Code Example

php
<?php
declare(strict_types=1);

class StringBuilder
{
    private string $str = '';

    public function append(string $s): static
    {
        $this->str .= $s;
        return $this;
    }

    public function prepend(string $s): static
    {
        $this->str = $s . $this->str;
        return $this;
    }

    public function upper(): static
    {
        $this->str = strtoupper($this->str);
        return $this;
    }

    public function build(): string
    {
        return $this->str;
    }
}

// Before PHP 8.4 — parentheses required around new expression
$result = (new StringBuilder())->append('hello')->append(' world')->upper()->build();

// PHP 8.4 — no wrapping parentheses needed
$result = new StringBuilder()->append('hello')->append(' world')->upper()->build();
echo $result; // "HELLO WORLD"

// With constructor arguments
class Formatter
{
    public function __construct(private string $prefix) {}
    public function format(string $s): string { return $this->prefix . $s; }
}
echo new Formatter('[LOG] ')->format('hello'); // "[LOG] hello"

// Property access without parentheses
class Config
{
    public string $env = 'production';
}
echo new Config()->env; // "production"

// Array access
class ArrayWrapper
{
    private array $data;
    public function __construct(array $data) { $this->data = $data; }
    public function offsetGet(int $i): mixed { return $this->data[$i]; }
}
// echo new ArrayWrapper([1,2,3])[0]; // also works in PHP 8.4