Bug #78859 [NEW]: Weird behavior with uninitialized typed properties and __set/__get
| From: | krmgns at gmail dot com | Date: | Fri, 22 Nov 2019 21:05:58 +0000 |
| Subject: | Bug #78859 [NEW]: Weird behavior with uninitialized typed properties and __set/__get | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-223864@lists.php.net to get a copy of this message | ||
From: krmgns at gmail dot com
Operating system: Ubuntu 18.04
PHP version: 7.4.0RC6
Package: *General Issues
Bug Type: Bug
Bug description:Weird behavior with uninitialized typed properties and __set/__get
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 bug report at https://bugs.php.net/bug.php?id=78859&edit=1
--
Fix committed: https://bugs.php.net/fix.php?id=78859&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=78859&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=78859&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=78859&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=78859&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=78859&r=support
Expected behavior: https://bugs.php.net/fix.php?id=78859&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=78859&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=78859&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=78859&r=globals
PHP version support discontinued: https://bugs.php.net/fix.php?id=78859&r=phptooold
Daylight Savings: https://bugs.php.net/fix.php?id=78859&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=78859&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=78859&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=78859&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=78859&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=78859&r=mysqlcfg