Re: [PHP-DEV] Constructor promoted property and default value
On Mon, Oct 23, 2023 at 6:22 PM Pierre wrote: > > Le 23/10/2023 à 18:11, Saki Takamachi a écrit : > >> If I understand your use case properly, you should be confused by > >> properties with default values that are not constructor-promoted as well ? > >> Am I wrong ? In this case, your problem is not with promoted properties ? > > If we specify it the way you say, the initial values of the constructor > > arguments will be available even when the constructor is not called. > > > > Such behavior felt a little counterintuitive. > > Which then would simply be the same behavior as properties when not > promoted but declared in the class body instead: > > ```php > > class Foo > { > public $val = 'abc'; > } > > $redis_foo = serialize(new Foo()); > > $foo = unserialize($redis_foo); > var_dump($foo->val); > // string(3) "abc" > > ``` > > Right ? > > What's the most disturbing in my opinion is that: `class Foo { public > string $val = 'abc' }` and `class Foo { public function > __construct(public string $val = 'abc' ) {}}` don't yield the same > behavior at the time. > > Regards, > > -- > > Pierre > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Here's a nice and simple example: https://3v4l.org/DU0tG class A { public string $default = 'default'; } class B { public function __construct(public string $default = 'default') {} } $a = new A(); echo "Original A: {$a->default}\n"; $b = new B(); echo "Original B: {$b->default}\n"; $a = (new ReflectionClass($a))->newInstanceWithoutConstructor(); echo "New A: {$a->default}\n"; $b = (new ReflectionClass($b))->newInstanceWithoutConstructor(); echo "New B: {$b->default}\n"; // crashes here Robert Landers Software Engineer Utrecht NL -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Constructor promoted property and default value
Le 23/10/2023 à 18:11, Saki Takamachi a écrit : If I understand your use case properly, you should be confused by properties with default values that are not constructor-promoted as well ? Am I wrong ? In this case, your problem is not with promoted properties ? If we specify it the way you say, the initial values of the constructor arguments will be available even when the constructor is not called. Such behavior felt a little counterintuitive. Which then would simply be the same behavior as properties when not promoted but declared in the class body instead: ```php class Foo { public $val = 'abc'; } $redis_foo = serialize(new Foo()); $foo = unserialize($redis_foo); var_dump($foo->val); // string(3) "abc" ``` Right ? What's the most disturbing in my opinion is that: `class Foo { public string $val = 'abc' }` and `class Foo { public function __construct(public string $val = 'abc' ) {}}` don't yield the same behavior at the time. Regards, -- Pierre -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Constructor promoted property and default value
> If I understand your use case properly, you should be confused by properties > with default values that are not constructor-promoted as well ? Am I wrong ? > In this case, your problem is not with promoted properties ? If we specify it the way you say, the initial values of the constructor arguments will be available even when the constructor is not called. ``` class Foo { public function __construct() { } } $foo = serialize(new Foo()); // save to redis update class Foo { public function __construct( public $val = 'abc' ) { } } $foo = unserialize($redis_foo); var_dump($foo->val); // string(3) "abc" // Doesn't it look like the constructor is being called? ``` Such behavior felt a little counterintuitive. Regards. Saki -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Constructor promoted property and default value
Le 23/10/2023 à 17:35, Saki Takamachi a écrit : Hi, Pierre You may have overlooked the existence of the magic method `__unserialize()`. Constructor is not the only way to create instances. When rebuilding a serialized object, you may need the initial values of properties. This can easily happen if you are using rolling updates and using Redis. The constructor is not called if `__unserialize()` is called. In this case, defining the initial value of a property as an argument to a constructor that is never called confuses us. Regards. Saki If I understand your use case properly, you should be confused by properties with default values that are not constructor-promoted as well ? Am I wrong ? In this case, your problem is not with promoted properties ? Regards, -- Pierre -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Constructor promoted property and default value
Hi, Pierre You may have overlooked the existence of the magic method `__unserialize()`. Constructor is not the only way to create instances. When rebuilding a serialized object, you may need the initial values of properties. This can easily happen if you are using rolling updates and using Redis. The constructor is not called if `__unserialize()` is called. In this case, defining the initial value of a property as an argument to a constructor that is never called confuses us. Regards. Saki -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Constructor promoted property and default value
Le 23/10/2023 à 17:16, Larry Garfield a écrit : 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 Thanks for the explanation ! I see, there is no easy answer, yet current state is, in my opinion, unintuitive for developers, and eventually unsatisfying when falling in this trap. That's sad that readonly properties make this not trivial. Maybe some kind of object construct-post-hook could detect those case and affect variables with default values ? I don't know enough PHP internals to give a rational answer to this problem through... Regards, -- Pierre
Re: [PHP-DEV] Constructor promoted property and default value
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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
[PHP-DEV] Constructor promoted property and default value
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") ? Regards, -- Pierre -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php