Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit de1c216

Browse files
joelwurtzKorbeil
authored andcommitted
[Serializer] Instantiator - Add an interface and default implementation to instantiate objects #30956
1 parent 5781cdd commit de1c216

File tree

4 files changed

+202
-199
lines changed

4 files changed

+202
-199
lines changed

src/Symfony/Component/Serializer/Instantiator/Instantiator.php

Lines changed: 52 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -10,131 +10,100 @@
1010
*/
1111

1212
namespace Symfony\Component\Serializer\Instantiator;
13+
1314
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
14-
use Symfony\Component\Serializer\Exception\LogicException;
1515
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
1616
use Symfony\Component\Serializer\Exception\RuntimeException;
17-
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
1817
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
19-
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
20-
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
18+
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
19+
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
20+
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
2121
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
22-
use Symfony\Component\Serializer\SerializerAwareInterface;
23-
use Symfony\Component\Serializer\SerializerAwareTrait;
2422

2523
/**
2624
* @author Jérôme Desjardins <[email protected]>
2725
*/
28-
class Instantiator implements InstantiatorInterface, SerializerAwareInterface
26+
class Instantiator implements InstantiatorInterface, DenormalizerAwareInterface
2927
{
28+
public const DEFAULT_CONSTRUCTOR_ARGUMENTS = AbstractObjectNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS;
29+
3030
use ObjectToPopulateTrait;
31-
use SerializerAwareTrait;
31+
use DenormalizerAwareTrait;
3232

3333
private $classDiscriminatorResolver;
3434
private $propertyListExtractor;
3535
private $nameConverter;
3636

37-
public function __construct(ClassDiscriminatorResolverInterface $classDiscriminatorResolver, PropertyListExtractorInterface $propertyListExtractor, NameConverterInterface $nameConverter)
37+
public function __construct(PropertyListExtractorInterface $propertyListExtractor = null, NameConverterInterface $nameConverter = null)
3838
{
39-
$this->classDiscriminatorResolver = $classDiscriminatorResolver;
4039
$this->propertyListExtractor = $propertyListExtractor;
4140
$this->nameConverter = $nameConverter;
4241
}
4342

43+
/**
44+
* {@inheritdoc}
45+
*/
4446
public function instantiate(string $class, $data, $format = null, array $context = [])
4547
{
46-
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
47-
if (!isset($data[$mapping->getTypeProperty()])) {
48-
throw new RuntimeException(sprintf('Type property "%s" not found for the abstract object "%s"', $mapping->getTypeProperty(), $class));
49-
}
50-
51-
$type = $data[$mapping->getTypeProperty()];
52-
if (null === ($mappedClass = $mapping->getClassForType($type))) {
53-
throw new RuntimeException(sprintf('The type "%s" has no mapped class for the abstract object "%s"', $type, $class));
54-
}
48+
$allowedAttributes = $this->propertyListExtractor ? $this->propertyListExtractor->getProperties($class, $context) : null;
49+
$reflectionClass = new \ReflectionClass($class);
50+
$constructor = $reflectionClass->getConstructor();
5551

56-
$class = $mappedClass;
52+
if (null === $constructor) {
53+
return new $class();
5754
}
5855

59-
$reflectionClass = new \ReflectionClass($class);
56+
$constructorParameters = $constructor->getParameters();
6057

61-
if (null !== $object = $this->extractObjectToPopulate($class, $context, AbstractNormalizer::OBJECT_TO_POPULATE)) {
62-
unset($context[AbstractNormalizer::OBJECT_TO_POPULATE]);
58+
$params = [];
59+
foreach ($constructorParameters as $constructorParameter) {
60+
$paramName = $constructorParameter->name;
61+
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
62+
$allowed = null === $allowedAttributes || \in_array($paramName, $allowedAttributes, true);
6363

64-
return $object;
65-
}
64+
if ($allowed && $constructorParameter->isVariadic() && \array_key_exists($key, $data)) {
65+
if (!\is_array($data[$paramName])) {
66+
throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name));
67+
}
6668

67-
$defaultConstructionArgumentKey = $context['defaultConstructionArgumentKey'] ?? AbstractNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS
68-
$allowedAttributes = $this->propertyListExtractor->getProperties($class, $context);
69-
$constructor = $reflectionClass->getConstructor();
70-
if ($constructor) {
71-
$constructorParameters = $constructor->getParameters();
72-
73-
$params = [];
74-
foreach ($constructorParameters as $constructorParameter) {
75-
$paramName = $constructorParameter->name;
76-
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
77-
78-
$allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes);
79-
if ($constructorParameter->isVariadic()) {
80-
if ($allowed && (isset($data[$key]) || \array_key_exists($key, $data))) {
81-
if (!\is_array($data[$paramName])) {
82-
throw new RuntimeException(sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name));
83-
}
84-
85-
$params = array_merge($params, $data[$paramName]);
86-
}
87-
} elseif ($allowed && (isset($data[$key]) || \array_key_exists($key, $data))) {
88-
$parameterData = $data[$key];
89-
if (null === $parameterData && $constructorParameter->allowsNull()) {
90-
$params[] = null;
91-
// Don't run set for a parameter passed to the constructor
92-
unset($data[$key]);
93-
continue;
94-
}
95-
96-
// Don't run set for a parameter passed to the constructor
97-
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
98-
unset($data[$key]);
99-
} elseif (\array_key_exists($key, $context[$defaultConstructionArgumentKey][$class] ?? [])) {
100-
$params[] = $context[$defaultConstructionArgumentKey][$class][$key];
101-
} elseif ($constructorParameter->isDefaultValueAvailable()) {
102-
$params[] = $constructorParameter->getDefaultValue();
103-
} else {
104-
throw new MissingConstructorArgumentsException(sprintf('Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', $class, $constructorParameter->name));
69+
$params = array_merge($params, $data[$paramName]);
70+
} elseif ($allowed && \array_key_exists($key, $data)) {
71+
$parameterData = $data[$key];
72+
73+
if (null === $parameterData && $constructorParameter->allowsNull()) {
74+
$params[] = null;
75+
76+
continue;
10577
}
106-
}
10778

108-
if ($constructor->isConstructor()) {
109-
return $reflectionClass->newInstanceArgs($params);
79+
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
80+
} elseif (\array_key_exists($key, $context[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
81+
$params[] = $context[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
82+
} elseif ($constructorParameter->isDefaultValueAvailable()) {
83+
$params[] = $constructorParameter->getDefaultValue();
84+
} else {
85+
throw new MissingConstructorArgumentsException(sprintf('Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', $class, $constructorParameter->name));
11086
}
111-
112-
return $constructor->invokeArgs(null, $params);
11387
}
11488

115-
return new $class();
116-
}
117-
118-
public function createChildContext(string $class, string $attribute, $parentData, array $parentContext = [])
119-
{
120-
if (isset($parentContext[AbstractNormalizer::ATTRIBUTES][$attribute])) {
121-
$parentContext[AbstractNormalizer::ATTRIBUTES] = $parentContext[AbstractNormalizer::ATTRIBUTES][$attribute];
122-
} else {
123-
unset($parentContext[AbstractNormalizer::ATTRIBUTES]);
89+
if ($constructor->isConstructor()) {
90+
return $reflectionClass->newInstanceArgs($params);
12491
}
12592

126-
return $parentContext;
93+
return $constructor->invokeArgs(null, $params);
12794
}
12895

12996
private function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
13097
{
13198
try {
13299
if (null !== $parameter->getClass()) {
133-
if (!$this->serializer instanceof DenormalizerInterface) {
134-
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $parameter->getClass(), self::class));
135-
}
136100
$parameterClass = $parameter->getClass()->getName();
137-
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createContext($context, $parameterName));
101+
102+
if (null === $this->denormalizer) {
103+
throw new MissingConstructorArgumentsException(sprintf('Could not create object of class "%s" of the parameter "%s".', $parameterClass, $parameterName));
104+
}
105+
106+
$parameterData = $this->denormalizer->denormalize($parameterData, $parameterClass, $format, $context);
138107
}
139108
} catch (\ReflectionException $e) {
140109
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
@@ -147,5 +116,4 @@ private function denormalizeParameter(\ReflectionClass $class, \ReflectionParame
147116

148117
return $parameterData;
149118
}
150-
151-
}
119+
}

src/Symfony/Component/Serializer/Instantiator/InstantiatorInterface.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@
1111

1212
namespace Symfony\Component\Serializer\Instantiator;
1313

14+
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
15+
1416
/**
1517
* @author Jérôme Desjardins <[email protected]>
1618
*/
1719
interface InstantiatorInterface
1820
{
21+
/**
22+
* Instantiate a new object.
23+
*
24+
* @throws MissingConstructorArgumentsException When some arguments are missing to use the constructor
25+
*
26+
* @return mixed
27+
*/
1928
public function instantiate(string $class, $data, $format = null, array $context = []);
20-
21-
public function createChildContext(string $class, $data, array $context = [], $attribute);
22-
23-
}
29+
}

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

Lines changed: 0 additions & 111 deletions
This file was deleted.

0 commit comments

Comments
 (0)