Skip to content

Commit 1034b31

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 symfony#51931 [VarExporter] Fix serializing objects that implement __sleep() and that are made lazy [String] Fix Inflector for 'icon'
2 parents d625fa0 + 668c441 commit 1034b31

File tree

11 files changed

+110
-25
lines changed

11 files changed

+110
-25
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ protected function getAttributes(object $object, ?string $format, array $context
269269
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
270270

271271
if (false !== $allowedAttributes) {
272-
$attributes = array_intersect($attributes, $allowedAttributes);
272+
$attributes = $attributes ? array_intersect($attributes, $allowedAttributes) : $allowedAttributes;
273273
}
274274

275275
if ($context['cache_key'] && \stdClass::class !== $class) {

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

+32-2
Original file line numberDiff line numberDiff line change
@@ -840,9 +840,39 @@ public function testDenormalizeWithCorrectOrderOfAttributeAndProperty()
840840
public function testNormalizeWithIgnoreAttributeAndPrivateProperties()
841841
{
842842
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
843-
$serializer = new Serializer([new ObjectNormalizer($classMetadataFactory)]);
843+
$normalizer = new ObjectNormalizer($classMetadataFactory);
844844

845-
$this->assertSame(['foo' => 'foo'], $serializer->normalize(new ObjectDummyWithIgnoreAttributeAndPrivateProperty()));
845+
$this->assertSame(['foo' => 'foo'], $normalizer->normalize(new ObjectDummyWithIgnoreAttributeAndPrivateProperty()));
846+
}
847+
848+
public function testNormalizeBasedOnAllowedAttributes()
849+
{
850+
$normalizer = new class() extends AbstractObjectNormalizer {
851+
protected function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false)
852+
{
853+
return ['foo'];
854+
}
855+
856+
protected function extractAttributes(object $object, string $format = null, array $context = []): array
857+
{
858+
return [];
859+
}
860+
861+
protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = [])
862+
{
863+
return $object->$attribute;
864+
}
865+
866+
protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = [])
867+
{
868+
}
869+
};
870+
871+
$object = new Dummy();
872+
$object->foo = 'foo';
873+
$object->bar = 'bar';
874+
875+
$this->assertSame(['foo' => 'foo'], $normalizer->normalize($object));
846876
}
847877

848878
public function testDenormalizeUntypedFormat()

src/Symfony/Component/String/Inflector/EnglishInflector.php

+3
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ final class EnglishInflector implements InflectorInterface
253253
// seasons (season), treasons (treason), poisons (poison), lessons (lesson)
254254
['nos', 3, true, true, 'sons'],
255255

256+
// icons (icon)
257+
['noc', 3, true, true, 'cons'],
258+
256259
// bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
257260
['no', 2, true, true, 'a'],
258261

src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ public static function pluralizeProvider()
295295
['tree', 'trees'],
296296
['waltz', 'waltzes'],
297297
['wife', 'wives'],
298+
['icon', 'icons'],
298299

299300
// test casing: if the first letter was uppercase, it should remain so
300301
['Man', 'Men'],

src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf

+24
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,30 @@
402402
<source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
403403
<target>Стойността на мрежовата маска трябва да бъде между {{ min }} и {{ max }}.</target>
404404
</trans-unit>
405+
<trans-unit id="104">
406+
<source>The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.</source>
407+
<target>Името на файла е твърде дълго. Трябва да съдържа не повече от {{ filename_max_length }} символ.|Името на файла е твърде дълго. Трябва да съдържа не повече от {{ filename_max_length }} символа.</target>
408+
</trans-unit>
409+
<trans-unit id="105">
410+
<source>The password strength is too low. Please use a stronger password.</source>
411+
<target>Сложността на паролата е твърде малка. Моля използвайте по-сложна парола.</target>
412+
</trans-unit>
413+
<trans-unit id="106">
414+
<source>This value contains characters that are not allowed by the current restriction-level.</source>
415+
<target>Стойността съдържа символи, които не са позволени от текущото ниво на ограничение.</target>
416+
</trans-unit>
417+
<trans-unit id="107">
418+
<source>Using invisible characters is not allowed.</source>
419+
<target>Използването на невидими символи не е позволено.</target>
420+
</trans-unit>
421+
<trans-unit id="108">
422+
<source>Mixing numbers from different scripts is not allowed.</source>
423+
<target>Смесването на числа от различни скриптове не е позволено.</target>
424+
</trans-unit>
425+
<trans-unit id="109">
426+
<source>Using hidden overlay characters is not allowed.</source>
427+
<target>Използването на скрити насложени символи не е позволено.</target>
428+
</trans-unit>
405429
</body>
406430
</file>
407431
</xliff>

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

+4-4
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
}

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

+11-8
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

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

src/Symfony/Component/VarExporter/LazyProxyTrait.php

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

src/Symfony/Component/VarExporter/ProxyHelper.php

+3
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

+10-2
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)