Bug #78859 [Opn->Nab]: Weird behavior with uninitialized typed properties and __set/__get
| From: | requinix@php.net | Date: | Fri, 22 Nov 2019 21:25:07 +0000 |
| Subject: | Bug #78859 [Opn->Nab]: Weird behavior with uninitialized typed properties and __set/__get | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-223865@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=78859&edit=1
ID: 78859
Updated by: requinix@php.net
Reported by: krmgns at gmail dot com
Summary: Weird behavior with uninitialized typed properties
and __set/__get
-Status: Open
+Status: Not a bug
Type: Bug
Package: *General Issues
Operating System: Ubuntu 18.04
-PHP Version: 7.4.0RC6
+PHP Version: 7.4.0RC4
Block user comment: N
Private report: N
New Comment:
Thank you for taking the time to report a problem with PHP.
Unfortunately you are not using a current version of PHP --
the problem might already be fixed. Please download a new
PHP version from http://www.php.net/downloads.php
If you are able to reproduce the bug with one of the latest
versions of PHP, please change the PHP version on this bug report
to the version you tested and change the status back to "Open".
Again, thank you for your continued support of PHP.
https://3v4l.org/VJFBK
Previous Comments:
------------------------------------------------------------------------
[2019-11-22 21:05:58] krmgns at gmail dot com
Description:
------------
Seems calling a constructor is triggering __set magic for uninitialized typed properties. So it does
not matter the property is public or private/protected.
I suppose the problem is __set/__get called before __construct when a type is given to a property.
Also I if remove __get then I get object(acme\Options)#1 (1) { ["stack"]=> array(1) {
["stack"]=> array(1) { ["one"]=> int(1) } } }.
Test script:
---------------
final class Options {
// This is OK but why redundant initialization?
// public array $stack = [];
public array $stack;
public function __construct(array $stack) {
$this->stack = $stack;
}
public function __set(string $name, $value) {
// if (empty($this->stack)) {
// // This line below yields: object(acme\Options)#1 (0) { ["stack"]=>
uninitialized(array) }.
// // return;
// // This line below yields: object(acme\Options)#1 (1) { ["stack"]=>
array(1) { ["stack"]=> array(1) { ["one"]=> int(1) } } }.
// // $this->stack = [];
// }
// This is solving problem but the purpose is not that also corrupting $stack structure
inserting a new sub-array.
// object(acme\Options)#1 (1) { ["stack"]=> array(2) { ["one"]=>
int(1) ["stack"]=> array(1) { ["one"]=> int(1) } } }
// $this->stack = $value;
// This is indicating that __set called before (before __construct).
// throw new \Exception();
// This is problematic part.
$this->stack[$name] = $value;
}
public function __get(string $name) {
// This is indicating that __get called before (before __construct).
// throw new \Exception();
return $this->stack[$name] ?? null;
}
}
var_dump(new Options(['one' => 1]));
Expected result:
----------------
object(acme\Options)#1 (1) {
["stack"]=>
array(1) {
["one"]=>
int(1)
}
}
Actual result:
--------------
PHP Notice: Indirect modification of overloaded property acme\Options::$stack has no effect in
/var/www/a.php on line 19
PHP Fatal error: Uncaught TypeError: Typed property acme\Options::$stack must be array, null used
in /var/www/a.php:19
Stack trace:
#0 /var/www/a.php(16): acme\Options->__set()
#1 /var/www/a.php(26): acme\Options->__construct()
#2 {main}
thrown in /var/www/a.php on line 19
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=78859&edit=1