Skip to content

Commit d6081c0

Browse files
Merge branch '6.3' into 6.4
* 6.3: [Serializer] Fix normalization relying on allowed attributes only [VarExporter] Work around php/php-src#12695 for lazy objects, fixing nullsafe-related behavior [Validator] Add missing translations for Bulgarian #51931 [VarExporter] Fix serializing objects that implement __sleep() and that are made lazy [String] Fix Inflector for 'icon'
2 parents ca57ddc + a00fd2d commit d6081c0

File tree

6 files changed

+49
-22
lines changed

6 files changed

+49
-22
lines changed

Internal/Hydrator.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,10 @@ public static function getPropertyScopes($class)
271271
$name = $property->name;
272272

273273
if (\ReflectionProperty::IS_PRIVATE & $flags) {
274-
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null];
274+
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $property];
275275
continue;
276276
}
277-
$propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null];
277+
$propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null, $property];
278278

279279
if (\ReflectionProperty::IS_PROTECTED & $flags) {
280280
$propertyScopes["\0*\0$name"] = $propertyScopes[$name];
@@ -288,8 +288,8 @@ public static function getPropertyScopes($class)
288288
if (!$property->isStatic()) {
289289
$name = $property->name;
290290
$readonlyScope = $property->isReadOnly() ? $class : null;
291-
$propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope];
292-
$propertyScopes[$name] ??= [$class, $name, $readonlyScope];
291+
$propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope, $property];
292+
$propertyScopes[$name] ??= [$class, $name, $readonlyScope, $property];
293293
}
294294
}
295295
}

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

LazyGhostTrait.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,18 @@ public function &__get($name): mixed
163163
$scope = Registry::getScope($propertyScopes, $class, $name);
164164
$state = $this->lazyObjectState ?? null;
165165

166-
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))
167-
&& LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)
168-
) {
169-
goto get_in_scope;
166+
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
167+
if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) {
168+
// Work around php/php-src#12695
169+
$property = $propertyScopes[null === $scope ? $name : "\0$scope\0$name"][3]
170+
?? (Hydrator::$propertyScopes[$this::class] = Hydrator::getPropertyScopes($this::class))[3];
171+
} else {
172+
$property = null;
173+
}
174+
175+
if ($property?->isInitialized($this) ?? LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)) {
176+
goto get_in_scope;
177+
}
170178
}
171179
}
172180

@@ -228,7 +236,9 @@ public function __set($name, $value): void
228236
$scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope);
229237
$state = $this->lazyObjectState ?? null;
230238

231-
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
239+
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
240+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
241+
) {
232242
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
233243
$state->initialize($this, $name, $readonlyScope ?? $scope);
234244
}
@@ -262,6 +272,7 @@ public function __isset($name): bool
262272
$state = $this->lazyObjectState ?? null;
263273

264274
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))
275+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
265276
&& LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)
266277
) {
267278
goto isset_in_scope;
@@ -291,7 +302,9 @@ public function __unset($name): void
291302
$scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope);
292303
$state = $this->lazyObjectState ?? null;
293304

294-
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
305+
if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
306+
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
307+
) {
295308
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
296309
$state->initialize($this, $name, $readonlyScope ?? $scope);
297310
}
@@ -346,7 +359,7 @@ public function __serialize(): array
346359
$data = [];
347360

348361
foreach (parent::__sleep() as $name) {
349-
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
362+
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
350363

351364
if (null === $k) {
352365
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE);

LazyProxyTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ public function __serialize(): array
295295
$data = [];
296296

297297
foreach (parent::__sleep() as $name) {
298-
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
298+
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
299299

300300
if (null === $k) {
301301
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE);

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);

Tests/LazyGhostTraitTest.php

Lines changed: 10 additions & 2 deletions
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()
@@ -146,7 +148,13 @@ public function testMagicClass(MagicClass $instance)
146148
$instance->bar = 123;
147149
$serialized = serialize($instance);
148150
$clone = unserialize($serialized);
149-
$this->assertSame(123, $clone->bar);
151+
152+
if ($instance instanceof ChildMagicClass) {
153+
// ChildMagicClass redefines the $data property but not the __sleep() method
154+
$this->assertFalse(isset($clone->bar));
155+
} else {
156+
$this->assertSame(123, $clone->bar);
157+
}
150158
}
151159

152160
public static function provideMagicClass()

0 commit comments

Comments
 (0)