Skip to content

Commit

Permalink
[Serializer] Fix SerializedPath not working with constructor arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
HypeMC committed May 6, 2023
1 parent 2407467 commit 0ff624c
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,19 +226,8 @@ public function normalize(mixed $object, string $format = null, array $context =

protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null)
{
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
if (!isset($data[$mapping->getTypeProperty()])) {
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);
}

$type = $data[$mapping->getTypeProperty()];
if (null === ($mappedClass = $mapping->getClassForType($type))) {
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);
}

if ($mappedClass !== $class) {
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
}
if ($class !== $mappedClass = $this->getMappedClass($data, $class, $context)) {
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
}

return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
Expand Down Expand Up @@ -319,11 +308,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
$normalizedData = $this->prepareForDenormalization($data);
$extraAttributes = [];

$reflectionClass = new \ReflectionClass($type);
$object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format);
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : $object::class;
$mappedClass = $this->getMappedClass($normalizedData, $type, $context);

$nestedAttributes = $this->getNestedAttributes($resolvedClass);
$nestedAttributes = $this->getNestedAttributes($mappedClass);
$nestedData = [];
$propertyAccessor = PropertyAccess::createPropertyAccessor();
foreach ($nestedAttributes as $property => $serializedPath) {
Expand All @@ -336,6 +323,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar

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

$object = $this->instantiateObject($normalizedData, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : $object::class;

foreach ($normalizedData as $attribute => $value) {
if ($this->nameConverter) {
$notConverted = $attribute;
Expand Down Expand Up @@ -792,4 +782,25 @@ private function removeNestedValue(array $path, array $data): array

return $data;
}

/**
* @return class-string
*/
private function getMappedClass(array $data, string $class, array $context): string
{
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
if (!isset($data[$mapping->getTypeProperty()])) {
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);
}

$type = $data[$mapping->getTypeProperty()];
if (null === $mappedClass = $mapping->getClassForType($type)) {
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);
}

return $mappedClass;
}

return $class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Fixtures\Annotations;

use Symfony\Component\Serializer\Annotation\SerializedPath;

class SerializedPathInConstructorDummy
{
public function __construct(
/**
* @SerializedPath("[one][two]")
*/
public $three,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Fixtures\Attributes;

use Symfony\Component\Serializer\Annotation\SerializedPath;

class SerializedPathInConstructorDummy
{
public function __construct(
#[SerializedPath('[one][two]')] public $three,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<attribute name="seven" serialized-path="[three][four]" />
</class>

<class name="Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy">
<attribute name="three" serialized-path="[one][two]" />
</class>

<class name="Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy">
<discriminator-map type-property="type">
<mapping type="first" class="Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
serialized_path: '[one][two]'
seven:
serialized_path: '[three][four]'
'Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy':
attributes:
three:
serialized_path: '[one][two]'
'Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy':
discriminator_map:
type_property: type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\MaxDepthDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedNameDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;

final class ClassMetadataFactoryCompilerTest extends TestCase
Expand Down Expand Up @@ -46,18 +47,20 @@ public function testItDumpMetadata()
$maxDepthDummyMetadata = $classMetatadataFactory->getMetadataFor(MaxDepthDummy::class);
$serializedNameDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedNameDummy::class);
$serializedPathDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathDummy::class);
$serializedPathInConstructorDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathInConstructorDummy::class);

$code = (new ClassMetadataFactoryCompiler())->compile([
$dummyMetadata,
$maxDepthDummyMetadata,
$serializedNameDummyMetadata,
$serializedPathDummyMetadata,
$serializedPathInConstructorDummyMetadata,
]);

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

$this->assertCount(4, $compiledMetadata);
$this->assertCount(5, $compiledMetadata);

$this->assertArrayHasKey(Dummy::class, $compiledMetadata);
$this->assertEquals([
Expand Down Expand Up @@ -99,5 +102,13 @@ public function testItDumpMetadata()
],
null,
], $compiledMetadata[SerializedPathDummy::class]);

$this->assertArrayHasKey(SerializedPathInConstructorDummy::class, $compiledMetadata);
$this->assertEquals([
[
'three' => [[], null, null, '[one][two]'],
],
null,
], $compiledMetadata[SerializedPathInConstructorDummy::class]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ public function testLoadSerializedPath()
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
}

public function testLoadSerializedPathInConstructor()
{
$classMetadata = new ClassMetadata($this->getNamespace().'\SerializedPathInConstructorDummy');
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
}

public function testLoadClassMetadataAndMerge()
{
$classMetadata = new ClassMetadata($this->getNamespace().'\GroupDummy');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ public function testSerializedPath()
$this->assertEquals('[three][four]', $attributesMetadata['seven']->getSerializedPath());
}

public function testSerializedPathInConstructor()
{
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertEquals('[one][two]', $attributesMetadata['three']->getSerializedPath());
}

public function testLoadDiscriminatorMap()
{
$classMetadata = new ClassMetadata(AbstractDummy::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ public function testSerializedPath()
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
}

public function testSerializedPathInConstructor()
{
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
}

public function testLoadDiscriminatorMap()
{
$classMetadata = new ClassMetadata(AbstractDummy::class);
Expand Down

0 comments on commit 0ff624c

Please sign in to comment.