Op di 17 feb 2026 om 20:26 schreef Rowan Tommins [IMSoP] <[email protected]>:
>
> 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