Bug #78904 [Nab]: Side-effects for uninitialised typed property

From: Date: Fri, 06 Dec 2019 13:34:25 +0000
Subject: Bug #78904 [Nab]: Side-effects for uninitialised typed property
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-224095@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=78904&edit=1 ID: 78904 User updated by: public at grik dot net Reported by: public at grik dot net Summary: Side-effects for uninitialised typed property Status: Not a bug Type: Bug Package: *General Issues PHP Version: 7.4.0 Block user comment: N Private report: N New Comment: @requinix Restricting access to uninitialised typed variable is OK. The problem is having the __get() called. @nikic Yes, I do have. 1. Consider a Request class from Laravel: https://github.com/laravel/framework/blob/master/src/Illuminate/Http/Request.php#L695 I extend it in an application to add custom behaviour, add a property. When the property is typed, I have a __get() call executed from the framework class, which is unexpected, because it differs from behavoir when the property has no type restriction. 2. __get() is extensively based in ORM implementations to map database data to the "virtual" properties. https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L1552 When we define a userland class property named after the table field name, we expect the __get() method not to be called. Virtial public properties may be a bad practice, but it is promoted extensively in several frameworks. Having __get() called for uninitialized variables introduces a new variable state, and as such it needs a way to be processed. Same as isset(), property_exists(), we need some property_initialized() function. Previous Comments: ------------------------------------------------------------------------ [2019-12-04 06:08:06] nikic@php.net Do you have any particular (not quite so reduced) examples where this causes problems in practice? We had some issues with __set interaction that were addressed, so possibly there is some tweak in behavior possibly here as well, it's just not clear from the bug report what the actual issue is. But generally, yes, initializing typed properties is obligatory, that's part of the point of having them. __get is called to allow some lazy initialization patterns, but shouldn't be relevant for most purposes. Is the issue here that __get is called instead of immediately throwing a "trying to access uninitialized property" error? ------------------------------------------------------------------------ [2019-12-03 22:40:37] requinix@php.net Works as intended. https://wiki.php.net/rfc/typed_properties_v2 Typed properties are never initialized to a default null, unlike untyped properties. That is why $y and $z do not show up through get_object_vars: they are uninitialized. No, it cannot simply return null because (a) the properties do not have the value of null and (b) null may not even be a valid value for the property. And as happens in some other languages, accessing an uninitialized property is not allowed. As for the fatal error, * ->y triggers __get, which returns null, which is valid for the "?string" type. No error. * ->x triggers __get, which returns null, which is not valid for the "string" type. Error. ------------------------------------------------------------------------ [2019-12-03 22:24:35] public at grik dot net Description: ------------ Unitialized properties make unpredictabe side effects. In fact, unitialized property is a Schrödinger property - it both exists and does not exist at the same time. All popular frameworks use magic getters in base classes. Having __get() called for uninitialized properties makes initialization in declaration to be obligatory, or face unpredictable side effects, as it was in Pascal 30 years ago. Scripting languages were inteneded to help avoid obligatory initialization in declaration. Test script: --------------- <?php abstract class Base{ public function __get($name){echo 'magic';} } class A extends Base{ public string $x; public ?string $y; public $z; } $A = new A; var_dump(get_object_vars($A)); //array(0) {} var_dump(property_exists($A,'x'));//true $A->z; // __get is not executed $A->y; // magic - __get() is executed $A->x; //Fatal error Expected result: ---------------- array(3) { ["x"]=> NULL ["y"]=> NULL ["z"]=> NULL } bool(true) Actual result: -------------- array(1) { ["z"]=> NULL } bool(true) magicmagic Fatal error: Uncaught TypeError: Typed property A::$x must be string, null used ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=78904&edit=1

« previous php.bugs (#224095) next »