Re: [IDEA for RFC] let the "new" operator fail when the __construct() function returns a value.

From: Date: Wed, 18 Feb 2026 15:23:43 +0000
Subject: Re: [IDEA for RFC] let the "new" operator fail when the __construct() function returns a value.
References: 1 2 3 4  Groups: php.internals 
Request: Send a blank email to internals+get-130089@lists.php.net to get a copy of this message
Op di 17 feb 2026 om 20:26 schreef Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk>: > > On 17/02/2026 06:59, Mirco Babin wrote: > > I disagree with the compiling part. The __construct() function can > > currently not have a return type, so the return type is implicitly > > "mixed". Changing that would be a BC break for those calling the > > __construct() function as a regular function. > > > It's a BC break either way - which is fine, if it only happens in PHP 9.0. > > > Consider this example in your draft RFC: > > ``` > class UnaffectedBaseClass > { > public function __construct() > { > return ['important']; > } > } > ``` > > Right now, there is nothing stopping someone calling that constructor as > new UnaffectedBaseClass. In PHP 9.0, that working code will > become an > error - a BC break. > > This is true even if the class is marked abstract: > > ``` > abstract class AbstractBaseClass > { > public function __construct() > { > return ['important']; > } > } > > class ValidSubClass extends AbstractBaseClass > { > } > > $it = new ValidSubClass(); // Error in PHP 9.0 > ``` I have added this example to the RFC. > In fact, the only way to guarantee a class is unaffected would be to > write a method called "__construct", but then use static analysis to > prove that nothing actually uses that method as a constructor. At the moment, without changing anything to Php, PHP Code Sniffer static analysis is the only option. Would this RFC be accepted, there are at least additional deprecation notices. But to be sure upfront, you still would have to use PHP Code Sniffer. > In which > case, why use the reserved name "__construct"? I'd go one step further. Why can "__construct" be called as a regular function? Why not prohibit that and make it a truly magical function? FYI: I have added "Ban __construct() as a regular function call" to the rejected features section. Banning the __construct() as regular function call, except when called as "parent::__construct()", is a rejected feature for this RFC. * Only the "new" keyword and "parent::__construct()" would then be allowed to call the %%__construct()%% constructor. * The __construct() would only serve as constructor. It would become truly a magic function. * Lazy ghost and ReflectionClass::newInstanceWithoutConstructor() would be a challenge. * This would go hand in hand with a implicit change to "void" return type declaration. * The goal of this RFC is minimal BC impact. This would have major impact. > My suggestion is that classes which are affected should show an error > *as soon as possible*, so that users are prompted to fix the definition. > > In the example above, I would much prefer to see the error (or > deprecation notice) as soon as AbstractBaseClass is compiled, rather > than waiting for something in the code base to call "new ValidSubClass". That's a personal preference. Sometimes I want compilation errors right away, but often I just want it to work. And if one web page crashes because of an incorrect "return," I'd rather fix it on the fly than get stuck with all sorts of compilation errors that I'm forced to resolve. Because the other webpages then do work, it is just 1 that is broken. It's about the obligation to fix it upfront versus fixing it when it goes wrong. Fixing it upfront is a black/white scenario, either it works or it fails. Fixing it when it goes wrong is a gray scenario, it works until it breaks. > Similarly, if there's a long constructor with a return statement inside > some rarely used conditional logic, I would prefer to be told > immediately that there is a mistake, rather than having a production > error when the rare situation happens. Normally, there's a test phase in between. There's always a chance that the test phase will already detect the faulty web page. > Once I'm told about the problem, I can choose between two things: > > 1) Stop the constructor returning a value, e.g. by replacing "return > foo();" with "foo(); return;" > 2) Rename the method to a non-reserved name, and if needed add a new > constructor which calls the method and discards the result There is a 3rd option: if ($calledAsRegularFunction_and_not_as_constructor) { return foo(); } > Thinking that through, having an error thrown only when calling with > new would actually be *more* disruptive. I would probably vote > "No" to > that version of the proposal. FYI: I have added "Changing to void return type declaration" to the rejected features section. There already was "aborting during compilation" in the rejected feature section. Conclusion: =========== There are two conflicting goals in the prologue: "Maximum warning" and "Minimal BC impact". You prioritize Maximum warning. I prioritize Minimal BC impact. Your points are correct, from the perspective of a new PHP project. However, reasoning from the perspective of a very old PHP project, every BC break is one too many. It works now, so why should something be changed? This old project has no security issues whatsoever. E.g. because it is an internal project, a cli tool, something that is not public. FYI: I updated the prologue. "This RFC prioritizes minimal BC impact above all else." Kind regards, Mirco Babin

« previous php.internals (#130089) next »