Re: Constructor promoted property and default value
| From: | Larry Garfield | Date: | Mon, 23 Oct 2023 15:16:26 +0000 |
| Subject: | Re: Constructor promoted property and default value | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-121444@lists.php.net to get a copy of this message | ||
On Mon, Oct 23, 2023, at 2:18 PM, Pierre wrote:
> Hello internals,
>
> I stumbled upon this behavior, if I write this:
>
> ```php
> class Foo
> {
> public ?string $prop = null;
>
> public function __construct(?string $prop = null)
> {
> $this->prop = $prop;
> }
> }
>
> class Bar extends Foo
> {
> public function __construct(
> public ?string $bar = null,
> ) {}
> }
>
> // Echoes nothing, but it works as expected.
> echo (new Bar())->prop;
> ```
>
> It works as intended, but if I replace the
Foo class using:
>
> ```php
> class Foo
> {
> public function __construct(
> public ?string $prop = null,
> ) {}
> }
> ```
>
> It won't work anymore and I have the following error:
>
> ```
> PHP Warning: Uncaught Error: Typed property Foo::$prop must not be
> accessed before initialization in php shell code:9
> ```
>
> If I understand it correctly:
> - in the first case, default value is attached to the object property,
> so if I omit its constructor, I have the default,
> - in the second case, default value is attached to the constructor
> parameter, and not to the object property, which means that in case the
> parent constructor is not called in the Bar class,
> $prop remains
> initialized.
>
> It doesn't sound like a bug, but I think that many people would actually
> expect otherwise: that the constructor promoted property keep their
> default even when constructor is not explicitly called.
>
> Is there any good reason behind this ? Wouldn't it be best to change
> this behavior ? Would it be a risk for backward compatibility (my guess
> is "not that much, probably not a all even") ?
Where this becomes a problem is readonly properties, since those are not allowed to have default
values. (That would make them constants with worse performance.) A solution would need to be able
to detect that the parent::__construct() isn't called, and then call it anyway, or at least
partially call it. Unfortunately, I can think of many cases where such a call would result in
unexpected behavior.
It might be possible to resolve, but it's definitely not simple, and it could easily lead to
weird behavior.
--Larry Garfield