Skip to content

Commit e52d5fa

Browse files
Merge branch '6.2' into 6.3
* 6.2: [Serializer] Fix SerializedPath not working with constructor arguments [FrameworkBundle] Fix Workflow without a marking store definition uses marking store definition of previously defined workflow [HttpFoundation] UrlHelper is now aware of RequestContext changes UrlHelper is now aware of RequestContext changes [Process] Stop the process correctly even if underlying input stream is not closed: Fix get sender name in turbosms notifier [Notifier] Update AmazonSns url in doc from de to en [PropertyInfo] Fix `PhpStanExtractor` when constructor has no docblock [PropertyAccess] Fix nullsafe operator on array index
2 parents 7592b55 + 7777df2 commit e52d5fa

File tree

10 files changed

+298
-35
lines changed

10 files changed

+298
-35
lines changed

Normalizer/AbstractObjectNormalizer.php

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ public function __construct(
130130

131131
$this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = array_merge($this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] ?? [], [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]);
132132

133-
if (null === $classDiscriminatorResolver && null !== $classMetadataFactory) {
134-
$classDiscriminatorResolver = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
133+
if ($classMetadataFactory) {
134+
$classDiscriminatorResolver ??= new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
135135
}
136136
$this->classDiscriminatorResolver = $classDiscriminatorResolver;
137137
$this->objectClassResolver = ($objectClassResolver ?? 'get_class')(...);
@@ -224,7 +224,7 @@ public function normalize(mixed $object, string $format = null, array $context =
224224
}
225225

226226
$preserveEmptyObjects = $context[self::PRESERVE_EMPTY_OBJECTS] ?? $this->defaultContext[self::PRESERVE_EMPTY_OBJECTS] ?? false;
227-
if ($preserveEmptyObjects && !\count($data)) {
227+
if ($preserveEmptyObjects && !$data) {
228228
return new \ArrayObject();
229229
}
230230

@@ -236,19 +236,8 @@ public function normalize(mixed $object, string $format = null, array $context =
236236
*/
237237
protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null)
238238
{
239-
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
240-
if (!isset($data[$mapping->getTypeProperty()])) {
241-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
242-
}
243-
244-
$type = $data[$mapping->getTypeProperty()];
245-
if (null === ($mappedClass = $mapping->getClassForType($type))) {
246-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
247-
}
248-
249-
if ($mappedClass !== $class) {
250-
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
251-
}
239+
if ($class !== $mappedClass = $this->getMappedClass($data, $class, $context)) {
240+
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
252241
}
253242

254243
return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
@@ -280,7 +269,7 @@ protected function getAttributes(object $object, ?string $format, array $context
280269

281270
$attributes = $this->extractAttributes($object, $format, $context);
282271

283-
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
272+
if ($mapping = $this->classDiscriminatorResolver?->getMappingForMappedObject($object)) {
284273
array_unshift($attributes, $mapping->getTypeProperty());
285274
}
286275

@@ -334,11 +323,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
334323
$normalizedData = $this->prepareForDenormalization($data);
335324
$extraAttributes = [];
336325

337-
$reflectionClass = new \ReflectionClass($type);
338-
$object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format);
339-
$resolvedClass = ($this->objectClassResolver)($object);
326+
$mappedClass = $this->getMappedClass($normalizedData, $type, $context);
340327

341-
$nestedAttributes = $this->getNestedAttributes($resolvedClass);
328+
$nestedAttributes = $this->getNestedAttributes($mappedClass);
342329
$nestedData = [];
343330
$propertyAccessor = PropertyAccess::createPropertyAccessor();
344331
foreach ($nestedAttributes as $property => $serializedPath) {
@@ -351,6 +338,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
351338

352339
$normalizedData = array_merge($normalizedData, $nestedData);
353340

341+
$object = $this->instantiateObject($normalizedData, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
342+
$resolvedClass = ($this->objectClassResolver)($object);
343+
354344
foreach ($normalizedData as $attribute => $value) {
355345
if ($this->nameConverter) {
356346
$notConverted = $attribute;
@@ -690,11 +680,8 @@ private function updateData(array $data, string $attribute, mixed $attributeValu
690680
*/
691681
private function isMaxDepthReached(array $attributesMetadata, string $class, string $attribute, array &$context): bool
692682
{
693-
$enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? false;
694-
if (
695-
!$enableMaxDepth
696-
|| !isset($attributesMetadata[$attribute])
697-
|| null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
683+
if (!($enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? false)
684+
|| null === $maxDepth = $attributesMetadata[$attribute]?->getMaxDepth()
698685
) {
699686
return false;
700687
}
@@ -770,7 +757,7 @@ private function isUninitializedValueError(\Error $e): bool
770757
*/
771758
private function getNestedAttributes(string $class): array
772759
{
773-
if (!$this->classMetadataFactory || !$this->classMetadataFactory->hasMetadataFor($class)) {
760+
if (!$this->classMetadataFactory?->hasMetadataFor($class)) {
774761
return [];
775762
}
776763

@@ -795,15 +782,30 @@ private function getNestedAttributes(string $class): array
795782
private function removeNestedValue(array $path, array $data): array
796783
{
797784
$element = array_shift($path);
798-
if ([] === $path) {
785+
if (!$path || !$data[$element] = $this->removeNestedValue($path, $data[$element])) {
799786
unset($data[$element]);
800-
} else {
801-
$data[$element] = $this->removeNestedValue($path, $data[$element]);
802-
if ([] === $data[$element]) {
803-
unset($data[$element]);
804-
}
805787
}
806788

807789
return $data;
808790
}
791+
792+
/**
793+
* @return class-string
794+
*/
795+
private function getMappedClass(array $data, string $class, array $context): string
796+
{
797+
if (!$mapping = $this->classDiscriminatorResolver?->getMappingForClass($class)) {
798+
return $class;
799+
}
800+
801+
if (null === $type = $data[$mapping->getTypeProperty()] ?? null) {
802+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
803+
}
804+
805+
if (null === $mappedClass = $mapping->getClassForType($type)) {
806+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
807+
}
808+
809+
return $mappedClass;
810+
}
809811
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures\Annotations;
13+
14+
use Symfony\Component\Serializer\Annotation\SerializedPath;
15+
16+
class SerializedPathInConstructorDummy
17+
{
18+
public function __construct(
19+
/**
20+
* @SerializedPath("[one][two]")
21+
*/
22+
public $three,
23+
) {
24+
}
25+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures\Attributes;
13+
14+
use Symfony\Component\Serializer\Annotation\SerializedPath;
15+
16+
class SerializedPathInConstructorDummy
17+
{
18+
public function __construct(
19+
#[SerializedPath('[one][two]')] public $three,
20+
) {
21+
}
22+
}

Tests/Fixtures/serialization.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
<attribute name="seven" serialized-path="[three][four]" />
3131
</class>
3232

33+
<class name="Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy">
34+
<attribute name="three" serialized-path="[one][two]" />
35+
</class>
36+
3337
<class name="Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy">
3438
<discriminator-map type-property="type">
3539
<mapping type="first" class="Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild" />

Tests/Fixtures/serialization.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
serialized_path: '[one][two]'
2323
seven:
2424
serialized_path: '[three][four]'
25+
'Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy':
26+
attributes:
27+
three:
28+
serialized_path: '[one][two]'
2529
'Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy':
2630
discriminator_map:
2731
type_property: type

Tests/Mapping/Factory/ClassMetadataFactoryCompilerTest.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\MaxDepthDummy;
2020
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedNameDummy;
2121
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathDummy;
22+
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy;
2223
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
2324

2425
final class ClassMetadataFactoryCompilerTest extends TestCase
@@ -46,18 +47,20 @@ public function testItDumpMetadata()
4647
$maxDepthDummyMetadata = $classMetatadataFactory->getMetadataFor(MaxDepthDummy::class);
4748
$serializedNameDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedNameDummy::class);
4849
$serializedPathDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathDummy::class);
50+
$serializedPathInConstructorDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathInConstructorDummy::class);
4951

5052
$code = (new ClassMetadataFactoryCompiler())->compile([
5153
$dummyMetadata,
5254
$maxDepthDummyMetadata,
5355
$serializedNameDummyMetadata,
5456
$serializedPathDummyMetadata,
57+
$serializedPathInConstructorDummyMetadata,
5558
]);
5659

5760
file_put_contents($this->dumpPath, $code);
5861
$compiledMetadata = require $this->dumpPath;
5962

60-
$this->assertCount(4, $compiledMetadata);
63+
$this->assertCount(5, $compiledMetadata);
6164

6265
$this->assertArrayHasKey(Dummy::class, $compiledMetadata);
6366
$this->assertEquals([
@@ -99,5 +102,13 @@ public function testItDumpMetadata()
99102
],
100103
null,
101104
], $compiledMetadata[SerializedPathDummy::class]);
105+
106+
$this->assertArrayHasKey(SerializedPathInConstructorDummy::class, $compiledMetadata);
107+
$this->assertEquals([
108+
[
109+
'three' => [[], null, null, '[one][two]'],
110+
],
111+
null,
112+
], $compiledMetadata[SerializedPathInConstructorDummy::class]);
102113
}
103114
}

Tests/Mapping/Loader/AnnotationLoaderTestCase.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ public function testLoadSerializedPath()
103103
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
104104
}
105105

106+
public function testLoadSerializedPathInConstructor()
107+
{
108+
$classMetadata = new ClassMetadata($this->getNamespace().'\SerializedPathInConstructorDummy');
109+
$this->loader->loadClassMetadata($classMetadata);
110+
111+
$attributesMetadata = $classMetadata->getAttributesMetadata();
112+
$this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
113+
}
114+
106115
public function testLoadClassMetadataAndMerge()
107116
{
108117
$classMetadata = new ClassMetadata($this->getNamespace().'\GroupDummy');

Tests/Mapping/Loader/XmlFileLoaderTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ public function testSerializedPath()
9494
$this->assertEquals('[three][four]', $attributesMetadata['seven']->getSerializedPath());
9595
}
9696

97+
public function testSerializedPathInConstructor()
98+
{
99+
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
100+
$this->loader->loadClassMetadata($classMetadata);
101+
102+
$attributesMetadata = $classMetadata->getAttributesMetadata();
103+
$this->assertEquals('[one][two]', $attributesMetadata['three']->getSerializedPath());
104+
}
105+
97106
public function testLoadDiscriminatorMap()
98107
{
99108
$classMetadata = new ClassMetadata(AbstractDummy::class);

Tests/Mapping/Loader/YamlFileLoaderTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ public function testSerializedPath()
108108
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
109109
}
110110

111+
public function testSerializedPathInConstructor()
112+
{
113+
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
114+
$this->loader->loadClassMetadata($classMetadata);
115+
116+
$attributesMetadata = $classMetadata->getAttributesMetadata();
117+
$this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
118+
}
119+
111120
public function testLoadDiscriminatorMap()
112121
{
113122
$classMetadata = new ClassMetadata(AbstractDummy::class);

0 commit comments

Comments
 (0)