Subject: [IDEA for RFC] let the "new" operator fail when the
__construct() function returns a value.
Date: 2 february 2026, 19:00h (Europe/Amsterdam)
Hello Internals,
The starting point of this idea is
https://github.com/php/php-src/issues/21090
By the way, I urge everyone to try out "yield 1" from the BC impact chapter.
That really demonstrates that the current "new" methodology is pointless.
Problem
The "new" operator internally calls the __construct() function. However,
the return value of the __construct() function is silently lost. This is
very error-prone; I personally made a major security mistake with this when
upgrading from Laravel 9 to Laravel 12.
To prevent future problems, my proposal is to have the "new" operator
explicitly issue warnings, and later have it explicitly abort when the
__construct() function returns a value.
A high-level overview of the "new" operator's operations. I haven't explored
this in detail; it's about the overall picture.
// High level picture of the "new" operator.
$newed = acquire_memory_and_initialize_object()
if (function_exists($newed, '__construct')) {
$newed->__construct(...);
// NOTICE that a return value from the __construct() function is silently
// discarded.
}
return $newed;
My proposal
Modify the "new" operator to the following. This will prevent a return value
from being silently lost. Instead, a warning will be issued and later the
"new" operator will throw a TypeError.
// High level picture of the adjusted "new" operator.
$newed = acquire_memory_and_initialize_object()
if (function_exists($newed, '__construct')) {
$__constructReturnValue = $newed->__construct(...);
if ($__constructReturnValue !== void) {
// DEPRECATION: returning a value from the __construct() function
// when called by the "new" operator is deprecated.
// LATER: throw new TypeError('the __construct() function must not
// return a value when called by the "new" operator.');
}
}
return $newed;
BC impact
Yes, there can be BC impact. If the __construct() function is also used as
a regular function. For example,
class SomeTheoreticalExample
{
public $uniqueId;
public function __construct(bool $returnSomeValue)
{
static $uniqueId = 0;
$this->uniqueId = $uniqueId;
$uniqueId++;
if ($returnSomeValue) {
// return some pointless value. Pointless, because it is
// silently discarded by the "new" operator.
return 'abc';
// return 1;
// return 1.23;
// return null;
// return true;
// return false;
// return ['a', 'b', 'c'];
// return [6 => 'six', 7 => 'seven', 67 => 'six seven'];
// return ['a' => 'a', 'b' => 'b', 'c' => 'c', 0 => 'Zero'];
// return SomeEnum::Spades;
// return function() {};
// return fn($a) => $a;
// return new DateTimeImmutable();
// return fopen('php://memory', 'w+');
// return $this;
// return &$this;
// return new SomeTheoreticalExample(false);
// Laravel 12 controller specific, of course this is very
// very wrong. It will NEVER redirect, and the flow
// continues to reach the (unauthorized) controller function.
// return redirect()->route('login');
// This is a very terrible case. Try it out yourself and
// watch the returned $newed->uniqueId.
// Spoiler alert: it won't be 0.
// yield 1;
}
}
}
// Before the RFC TypeError fact: nothing.
// After the RFC fact: will fail.
$newed = new SomeTheoreticalExample(true);
// Before the RFC TypeError fact: nothing.
// After the RFC fact: never called, because of the previous statement failure.
$someReasonToCall__ConstructAgain = $newed->__construct(true);
RFC
I've been asked to follow the RFC process. And I've been told that the
rejected PHP RFC Make constructors and destructors return void
( https://wiki.php.net/rfc/make_ctor_ret_void ) rejects my proposal.
However, my proposal is explicitly about the "new" operator and not
about return types. It does, however, concern return values.
I am very very reluctant to follow the RFC process myself:
- It would take up a lot of my free time. I would write the RFC first in
Dutch, my native language, and then translate it into English. This is
because I speak and write English, but don't understand all the nuances
of (American/Australian/British/Canadian/Irish/Nigerian/...) English. - While I do have some knowledge of C and the php-src codebase, writing a
PR to implement the RFC would take up a significant amount of my
free time. - The True Async RFC has demonstrated that the author of an RFC must have
a certain degree of trustworthiness. And I, as a passerby, have not
established any trustworthiness. - The voting process of an RFC is highly uncertain and can lead to
rejection. The whole process could ultimately be a complete waste
of my time.
Anyone who would like to create an RFC for this, please go ahead.
Yours sincerely,
Mirco Babin