1--TEST-- 2Lazy objects: Pre-initialization reference source types are properly handled (no initialization exception) 3--FILE-- 4<?php 5 6class C { 7 public ?C $a; 8 public ?C $b; 9 public $c; 10 public function __construct() { 11 $this->a = null; 12 unset($this->b); 13 $this->b = null; 14 } 15} 16 17function test(string $name, object $obj) { 18 $reflector = new ReflectionClass(C::class); 19 20 printf("# %s:\n", $name); 21 22 $reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, null); 23 $refA = &$obj->a; 24 $reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, null); 25 $refB = &$obj->b; 26 27 var_dump($obj); 28 var_dump($obj->c); 29 var_dump($obj); 30 31 try { 32 // $refA retained its reference source type (except for the proxy 33 // case: its the responsibility of the initializer to propagate 34 // pre-initialized properties to the instance) 35 $refA = 1; 36 } catch (\Error $e) { 37 printf("%s: %s\n", $e::class, $e->getMessage()); 38 } 39 40 // source type was not duplicated 41 unset($obj->a); 42 $refA = 1; 43 44 $refB = 1; 45} 46 47$reflector = new ReflectionClass(C::class); 48 49$obj = $reflector->newLazyGhost(function ($obj) { 50 var_dump("initializer"); 51 $obj->__construct(); 52}); 53 54test('Ghost', $obj); 55 56$obj = $reflector->newLazyProxy(function ($obj) { 57 var_dump("initializer"); 58 return new C(null); 59}); 60 61test('Proxy', $obj); 62--EXPECTF-- 63# Ghost: 64lazy ghost object(C)#%d (2) { 65 ["a"]=> 66 &NULL 67 ["b"]=> 68 &NULL 69} 70string(11) "initializer" 71NULL 72object(C)#%d (3) { 73 ["a"]=> 74 &NULL 75 ["b"]=> 76 NULL 77 ["c"]=> 78 NULL 79} 80TypeError: Cannot assign int to reference held by property C::$a of type ?C 81# Proxy: 82lazy proxy object(C)#%d (2) { 83 ["a"]=> 84 &NULL 85 ["b"]=> 86 &NULL 87} 88string(11) "initializer" 89NULL 90lazy proxy object(C)#%d (1) { 91 ["instance"]=> 92 object(C)#%d (3) { 93 ["a"]=> 94 NULL 95 ["b"]=> 96 NULL 97 ["c"]=> 98 NULL 99 } 100} 101