8000 [Serializer] Serialize and deserialize from abstract classes · symfony/symfony@4c6e05b · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c6e05b

Browse files
srozefabpot
authored andcommitted
[Serializer] Serialize and deserialize from abstract classes
1 parent a603ba0 commit 4c6e05b

27 files changed

+853
-9
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
use Symfony\Component\Security\Core\Security;
6363
use Symfony\Component\Serializer\Encoder\DecoderInterface;
6464
use Symfony\Component\Serializer\Encoder\EncoderInterface;
65+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
6566
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
6667
use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer;
6768
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@@ -1153,6 +1154,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
11531154
$container->removeDefinition('serializer.normalizer.dateinterval');
11541155
}
11551156

1157+
if (!class_exists(ClassDiscriminatorFromClassMetadata::class)) {
1158+
$container->removeAlias('Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface');
1159+
$container->removeDefinition('serializer.mapping.class_discriminator_resolver');
1160+
}
1161+
11561162
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
11571163

11581164
if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {

src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml

Lines changed: 7 additions & 0 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424

2525
<service id="serializer.property_accessor" alias="property_accessor" />
2626

27+
<!-- Discriminator Map -->
28+
<service id="serializer.mapping.class_discriminator_resolver" class="Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata">
29+
<argument type="service" id="serializer.mapping.class_metadata_factory" />
30+
</service>
31+
<service id="Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface" alias="serializer.mapping.class_discriminator_resolver" />
32+
2733
<!-- Normalizer -->
2834
<service id="serializer.normalizer.dateinterval" class="Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer">
2935
<!-- Run before serializer.normalizer.object -->
@@ -50,6 +56,7 @@
5056
<argument>null</argument> <!-- name converter -->
5157
<argument type="service" id="serializer.property_accessor" />
5258
<argument type="service" id="property_info" on-invalid="ignore" />
59+
<argument type="service" id="serializer.mapping.class_discriminator_resolver" on-invalid="ignore" />
5360

5461
<!-- Run after all custom normalizers -->
5562
<tag name="serializer.normalizer" priority="-1000" />
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Annotation;
13+
14+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15+
16+
/**
17+
* Annotation class for @DiscriminatorMap().
18+
*
19+
* @Annotation
20+
* @Target({"CLASS"})
21+
*
22+
* @author Samuel Roze <samuel.roze@gmail.com>
23+
*/
24+
class DiscriminatorMap
25+
{
26+
/**
27+
* @var string
28+
*/
29+
private $typeProperty;
30+
31+
/**
32+
* @var array
33+
*/
34+
private $mapping;
35+
36+
/**
37+
* @param array $data
38+
*
39+
* @throws InvalidArgumentException
40+
*/
41+
public function __construct(array $data)
42+
{
43+
if (empty($data['typeProperty'])) {
44+
throw new InvalidArgumentException(sprintf('Parameter "typeProperty" of annotation "%s" cannot be empty.', get_class($this)));
45+
}
46+
47+
if (empty($data['mapping'])) {
48+
throw new InvalidArgumentException(sprintf('Parameter "mapping" of annotation "%s" cannot be empty.', get_class($this)));
49+
}
50+
51+
$this->typeProperty = $data['typeProperty'];
52+
$this->mapping = $data['mapping'];
53+
}
54+
55+
public function getTypeProperty(): string
56+
{
57+
return $this->typeProperty;
58+
}
59+
60+
public function getMapping(): array
61+
{
62+
return $this->mapping;
63+
}
64+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Mapping;
13+
14+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
15+
16+
/**
17+
* @author Samuel Roze <samuel.roze@gmail.com>
18+
*/
19+
class ClassDiscriminatorFromClassMetadata implements ClassDiscriminatorResolverInterface
20+
{
21+
/**
22+
* @var ClassMetadataFactoryInterface
23+
*/
24+
private $classMetadataFactory;
25+
private $mappingForMappedObjectCache = array();
26+
27+
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
28+
{
29+
$this->classMetadataFactory = $classMetadataFactory;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function getMappingForClass(string $class): ?ClassDiscriminatorMapping
36+
{
37+
if ($this->classMetadataFactory->hasMetadataFor($class)) {
38+
return $this->classMetadataFactory->getMetadataFor($class)->getClassDiscriminatorMapping();
39+
}
40+
41+
return null;
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping
48+
{
49+
if ($this->classMetadataFactory->hasMetadataFor($object)) {
50+
$metadata = $this->classMetadataFactory->getMetadataFor($object);
51+
52+
if (null !== $metadata->getClassDiscriminatorMapping()) {
53+
return $metadata->getClassDiscriminatorMapping();
54+
}
55+
}
56+
57+
$cacheKey = is_object($object) ? get_class($object) : $object;
58+
if (!array_key_exists($cacheKey, $this->mappingForMappedObjectCache)) {
59+
$this->mappingForMappedObjectCache[$cacheKey] = $this->resolveMappingForMappedObject($object);
60+
}
61+
62+
return $this->mappingForMappedObjectCache[$cacheKey];
63+
}
64+
65+
/**
66+
* {@inheritdoc}
67+
*/
68+
public function getTypeForMappedObject($object): ?string
69+
{
70+
if (null === $mapping = $this->getMappingForMappedObject($object)) {
71+
return null;
72+
}
73+
74+
return $mapping->getMappedObjectType($object);
75+
}
76+
77+
private function resolveMappingForMappedObject($object)
78+
{
79+
$reflectionClass = new \ReflectionClass($object);
80+
if ($parentClass = $reflectionClass->getParentClass()) {
81+
return $this->getMappingForMappedObject($parentClass->getName());
82+
}
83+
84+
foreach ($reflectionClass->getInterfaceNames() as $interfaceName) {
85+
if (null !== ($interfaceMapping = $this->getMappingForMappedObject($interfaceName))) {
86+
return $interfaceMapping;
87+
}
88+
}
89+
90+
return null;
91+
}
92+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Mapping;
13+
14+
/**
15+
* @author Samuel Roze <samuel.roze@gmail.com>
16+
*/
17+
class ClassDiscriminatorMapping
18+
{
19+
private $typeProperty;
20+
private $typesMapping;
21+
22+
public function __construct(string $typeProperty, array $typesMapping = array())
23+
{
24+
$this->typeProperty = $typeProperty;
25+
$this->typesMapping = $typesMapping;
26+
}
27+
28+
public function getTypeProperty(): string
29+
{
30+
return $this->typeProperty;
31+
}
32+
33+
public function getClassForType(string $type): ?string
34+
{
35+
if (isset($this->typesMapping[$type])) {
36+
return $this->typesMapping[$type];
37+
}
38+
39+
return null;
40+
}
41+
42+
/**
43+
* @param object|string $object
44+
*
45+
* @return string|null
46+
*/
47+
public function getMappedObjectType($object): ?string
48+
{
49+
foreach ($this->typesMapping as $type => $typeClass) {
50+
if (is_a($object, $typeClass)) {
51+
return $type;
52+
}
53+
}
54+
55+
return null;
56+
}
57+
58+
public function getTypesMapping(): array
59+
{
60+
return $this->typesMapping;
61+
}
62+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Mapping;
13+
14+
/**
15+
* Knows how to get the class discriminator mapping for classes and objects.
16+
*
17+
* @author Samuel Roze <samuel.roze@gmail.com>
18+
*/
19+
interface ClassDiscriminatorResolverInterface
20+
{
21+
/**
22+
* @param string $class
23+
*
24+
* @return ClassDiscriminatorMapping|null
25+
*/
26+
public function getMappingForClass(string $class): ?ClassDiscriminatorMapping;
27+
28+
/**
29+
* @param object|string $object
30+
*
31+
* @return ClassDiscriminatorMapping|null
32+
*/
33+
public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping;
34+
35+
/**
36+
* @param object|string $object
37+
*
38+
* @return string|null
39+
*/
40+
public function getTypeForMappedObject($object): ?string;
41+
}

src/Symfony/Component/Serializer/Mapping/ClassMetadata.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,25 @@ class ClassMetadata implements ClassMetadataInterface
3939
*/
4040
private $reflClass;
4141

42-
public function __construct(string $class)
42+
/**
43+
* @var ClassDiscriminatorMapping|null
44+
*
45+
* @internal This property is public in order to reduce the size of the
46+
* class' serialized representation. Do not access it. Use
47+
* {@link getClassDiscriminatorMapping()} instead.
48+
*/
49+
public $classDiscriminatorMapping;
50+
51+
/**
52+
* Constructs a metadata for the given class.
53+
*
54+
* @param string $class
55+
* @param ClassDiscriminatorMapping|null $classDiscriminatorMapping
56+
*/
D7AE 57+
public function __construct(string $class, ClassDiscriminatorMapping $classDiscriminatorMapping = null)
4358
{
4459
$this->name = $class;
60+
$this->classDiscriminatorMapping = $classDiscriminatorMapping;
4561
}
4662

4763
/**
@@ -94,6 +110,22 @@ public function getReflectionClass()
94110
return $this->reflClass;
95111
}
96112

113+
/**
114+
* {@inheritdoc}
115+
*/
116+
public function getClassDiscriminatorMapping()
117+
{
118+
return $this->classDiscriminatorMapping;
119+
}
120+
121+
/**
122+
* {@inheritdoc}
123+
*/
124+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null)
125+
{
126+
$this->classDiscriminatorMapping = $mapping;
127+
}
128+
97129
/**
98130
* Returns the names of the properties that should be serialized.
99131
*
@@ -104,6 +136,7 @@ public function __sleep()
104136
return array(
105137
'name',
106138
'attributesMetadata',
139+
'classDiscriminatorMapping',
107140
);
108141
}
109142
}

src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,14 @@ public function merge(ClassMetadataInterface $classMetadata);
5454
* @return \ReflectionClass
5555
*/
5656
public function getReflectionClass();
57+
58+
/**
59+
* @return ClassDiscriminatorMapping|null
60+
*/
61+
public function getClassDiscriminatorMapping();
62+
63+
/**
64+
* @param ClassDiscriminatorMapping|null $mapping
65+
*/
66+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null);
5767
}

0 commit comments

Comments
 (0)
0