Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 491eb44

Browse files
[VarExporter] Work around php/php-src#12695 for lazy objects, fixing nullsafe-related behavior
1 parent 9145a88 commit 491eb44

File tree

5 files changed

+41
-19
lines changed

5 files changed

+41
-19
lines changed

src/Symfony/Component/VarExporter/Internal/Hydrator.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,13 @@ public static function getPropertyScopes($class)
269269
continue;
270270
}
271271
$name = $property->name;
272+
$isInitialized = $property->isInitialized(...); // Work around php/php-src#12695
272273

273274
if (\ReflectionProperty::IS_PRIVATE & $flags) {
274-
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null];
275+
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $isInitialized];
275276
continue;
276277
}
277-
$propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null];
278+
$propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null, $isInitialized];
278279

279280
if (\ReflectionProperty::IS_PROTECTED & $flags) {
280281
$propertyScopes["\0*\0$name"] = $propertyScopes[$name];
@@ -288,8 +289,9 @@ public static function getPropertyScopes($class)
288289
if (!$property->isStatic()) {
289290
$name = $property->name;
290291
$readonlyScope = $property->isReadOnly() ? $class : null;
291-
$propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope];
292-
$propertyScopes[$name] ??= [$class, $name, $readonlyScope];
292+
$isInitialized = $property->isInitialized(...);
293+
$propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope, $isInitialized];
294+
$propertyScopes[$name] ??= [$class, $name, $readonlyScope, $isInitialized];
293295
}
294296
}
295297
}

src/Symfony/Component/VarExporter/Internal/LazyObjectState.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,29 +65,32 @@ public function initialize($instance, $propertyName, $propertyScope)
6565
return $this->status = self::STATUS_INITIALIZED_PARTIAL;
6666
}
6767

68-
$status = self::STATUS_UNINITIALIZED_PARTIAL;
69-
7068
if ($initializer = $this->initializer["\0"] ?? null) {
7169
if (!\is_array($values = $initializer($instance, LazyObjectRegistry::$defaultProperties[$class]))) {
7270
throw new \TypeError(sprintf('The lazy-initializer defined for instance of "%s" must return an array, got "%s".', $class, get_debug_type($values)));
7371
}
7472
$properties = (array) $instance;
7573
foreach ($values as $key => $value) {
76-
if ($k === $key) {
77-
$status = self::STATUS_INITIALIZED_PARTIAL;
78-
}
7974
if (!\array_key_exists($key, $properties) && [$scope, $name, $readonlyScope] = $propertyScopes[$key] ?? null) {
8075
$scope = $readonlyScope ?? ('*' !== $scope ? $scope : $class);
8176
$accessor = LazyObjectRegistry::$classAccessors[$scope] ??= LazyObjectRegistry::getClassAccessors($scope);
8277
$accessor['set']($instance, $name, $value);
78+
79+
if ($k === $key) {
80+
$this->status = self::STATUS_INITIALIZED_PARTIAL;
81+
}
8382
}
8483
}
8584
}
8685

87-
return $status;
86+
return $this->status;
87+
}
88+
89+
if (self::STATUS_INITIALIZED_PARTIAL === $this->status) {
90+
return self::STATUS_INITIALIZED_PARTIAL;
8891
}
8992

90-
$this->status = self::STATUS_INITIALIZED_FULL;
93+
$this->status = self::STATUS_INITIALIZED_PARTIAL;
9194

9295
try {
9396
if ($defaultProperties = array_diff_key(LazyObjectRegistry::$defaultProperties[$instance::class], $this->skippedProperties)) {
@@ -102,7 +105,7 @@ public function initialize($instance, $propertyName, $propertyScope)
102105
throw $e;
103106
}
104107

105-
return self::STATUS_INITIALIZED_FULL;
108+
return $this->status = self::STATUS_INITIALIZED_FULL;
106109
}
107110

108111
public function reset($instance): void

src/Symfony/Component/VarExporter/LazyGhostTrait.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,17 @@ public function &__get($name): mixed
172172
$scope = Registry::getScope($propertyScopes, $class, $name);
173173
$state = $this->lazyObjectState ?? null;
174174

175-
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))
176-
&& LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)
177-
) {
178-
goto get_in_scope;
175+
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
176+
if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) {
177+
$isInitialized = $propertyScopes[null === $scope ? $name : "\0$scope\0$name"][3]
178+
?? (Hydrator::$propertyScopes[$this::class] = Hydrator::getPropertyScopes($this::class))[3];
179+
} else {
180+
$isInitialized = null;
181+
}
182+
183+
if ($isInitialized?->__invoke($this) ?? LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)) {
184+
goto get_in_scope;
185+
}
179186
}
180187
}
181188

@@ -237,7 +244,9 @@ public function __set($name, $value): void
237244
$scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope);
238245
$state = $this->lazyObjectState ?? null;
239246

240-
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
247+
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
248+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
249+
) {
241250
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
242251
$state->initialize($this, $name, $readonlyScope ?? $scope);
243252
}
@@ -271,6 +280,7 @@ public function __isset($name): bool
271280
$state = $this->lazyObjectState ?? null;
272281

273282
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))
283+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
274284
&& LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)
275285
) {
276286
goto isset_in_scope;
@@ -300,7 +310,9 @@ public function __unset($name): void
300310
$scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope);
301311
$state = $this->lazyObjectState ?? null;
302312

303-
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
313+
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
314+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
315+
) {
304316
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
305317
$state->initialize($this, $name, $readonlyScope ?? $scope);
306318
}

src/Symfony/Component/VarExporter/ProxyHelper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ private static function exportPropertyScopes(string $parent): string
322322
{
323323
$propertyScopes = Hydrator::$propertyScopes[$parent] ??= Hydrator::getPropertyScopes($parent);
324324
uksort($propertyScopes, 'strnatcmp');
325+
foreach ($propertyScopes as $k => $v) {
326+
unset($propertyScopes[$k][3]);
327+
}
325328
$propertyScopes = VarExporter::export($propertyScopes);
326329
$propertyScopes = str_replace(VarExporter::export($parent), 'parent::class', $propertyScopes);
327330
$propertyScopes = preg_replace("/(?|(,)\n( ) |\n |,\n (\]))/", '$1$2', $propertyScopes);

src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ public function testUnsetPublic()
6565

6666
$this->assertSame(["\0".TestClass::class."\0lazyObjectState"], array_keys((array) $instance));
6767
unset($instance->public);
68-
$this->assertFalse(isset($instance->public));
6968
$this->assertSame(4, $instance->publicReadonly);
69+
$this->expectException(\BadMethodCallException::class);
70+
$this->expectExceptionMessage('__isset(public)');
71+
isset($instance->public);
7072
}
7173

7274
public function testSetPublic()

0 commit comments

Comments
 (0)