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

Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[Serializer] Instantiator - Add an interface and default implementati…
…on to instantiate objects #30956
  • Loading branch information
joelwurtz authored and Korbeil committed Oct 12, 2020
commit 30d66584570752ffc4e6106362271bd20860722c
136 changes: 52 additions & 84 deletions src/Symfony/Component/Serializer/Instantiator/Instantiator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,131 +10,100 @@
*/

namespace Symfony\Component\Serializer\Instantiator;

use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerAwareTrait;

/**
* @author Jérôme Desjardins <[email protected]>
*/
class Instantiator implements InstantiatorInterface, SerializerAwareInterface
class Instantiator implements InstantiatorInterface, DenormalizerAwareInterface
{
public const DEFAULT_CONSTRUCTOR_ARGUMENTS = AbstractObjectNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS;

use ObjectToPopulateTrait;
use SerializerAwareTrait;
use DenormalizerAwareTrait;

private $classDiscriminatorResolver;
private $propertyListExtractor;
private $nameConverter;

public function __construct(ClassDiscriminatorResolverInterface $classDiscriminatorResolver, PropertyListExtractorInterface $propertyListExtractor, NameConverterInterface $nameConverter)
public function __construct(PropertyListExtractorInterface $propertyListExtractor = null, NameConverterInterface $nameConverter = null)
{
$this->classDiscriminatorResolver = $classDiscriminatorResolver;
$this->propertyListExtractor = $propertyListExtractor;
$this->nameConverter = $nameConverter;
}

/**
* {@inheritdoc}
*/
public function instantiate(string $class, $data, $format = null, array $context = [])
{
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
if (!isset($data[$mapping->getTypeProperty()])) {
throw new RuntimeException(sprintf('Type property "%s" not found for the abstract object "%s"', $mapping->getTypeProperty(), $class));
}

$type = $data[$mapping->getTypeProperty()];
if (null === ($mappedClass = $mapping->getClassForType($type))) {
throw new RuntimeException(sprintf('The type "%s" has no mapped class for the abstract object "%s"', $type, $class));
}
$allowedAttributes = $this->propertyListExtractor ? $this->propertyListExtractor->getProperties($class, $context) : null;
$reflectionClass = new \ReflectionClass($class);
$constructor = $reflectionClass->getConstructor();

$class = $mappedClass;
if (null === $constructor) {
return new $class();
}

$reflectionClass = new \ReflectionClass($class);
$constructorParameters = $constructor->getParameters();

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

return $object;
}
if ($allowed && $constructorParameter->isVariadic() && \array_key_exists($key, $data)) {
if (!\is_array($data[$paramName])) {
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));
}

$defaultConstructionArgumentKey = $context['defaultConstructionArgumentKey'] ?? AbstractNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS
$allowedAttributes = $this->propertyListExtractor->getProperties($class, $context);
$constructor = $reflectionClass->getConstructor();
if ($constructor) {
$constructorParameters = $constructor->getParameters();

$params = [];
foreach ($constructorParameters as $constructorParameter) {
$paramName = $constructorParameter->name;
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;

$allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes);
if ($constructorParameter->isVariadic()) {
if ($allowed && (isset($data[$key]) || \array_key_exists($key, $data))) {
if (!\is_array($data[$paramName])) {
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));
}

$params = array_merge($params, $data[$paramName]);
}
} elseif ($allowed && (isset($data[$key]) || \array_key_exists($key, $data))) {
$parameterData = $data[$key];
if (null === $parameterData && $constructorParameter->allowsNull()) {
$params[] = null;
// Don't run set for a parameter passed to the constructor
unset($data[$key]);
continue;
}

// Don't run set for a parameter passed to the constructor
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
unset($data[$key]);
} elseif (\array_key_exists($key, $context[$defaultConstructionArgumentKey][$class] ?? [])) {
$params[] = $context[$defaultConstructionArgumentKey][$class][$key];
} elseif ($constructorParameter->isDefaultValueAvailable()) {
$params[] = $constructorParameter->getDefaultValue();
} else {
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));
$params = array_merge($params, $data[$paramName]);
} elseif ($allowed && \array_key_exists($key, $data)) {
$parameterData = $data[$key];

if (null === $parameterData && $constructorParameter->allowsNull()) {
$params[] = null;

continue;
}
}

if ($constructor->isConstructor()) {
return $reflectionClass->newInstanceArgs($params);
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
} elseif (\array_key_exists($key, $context[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
$params[] = $context[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
} elseif ($constructorParameter->isDefaultValueAvailable()) {
$params[] = $constructorParameter->getDefaultValue();
} else {
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));
}

return $constructor->invokeArgs(null, $params);
}

return new $class();
}

public function createChildContext(string $class, string $attribute, $parentData, array $parentContext = [])
{
if (isset($parentContext[AbstractNormalizer::ATTRIBUTES][$attribute])) {
$parentContext[AbstractNormalizer::ATTRIBUTES] = $parentContext[AbstractNormalizer::ATTRIBUTES][$attribute];
} else {
unset($parentContext[AbstractNormalizer::ATTRIBUTES]);
if ($constructor->isConstructor()) {
return $reflectionClass->newInstanceArgs($params);
}

return $parentContext;
return $constructor->invokeArgs(null, $params);
}

private function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
{
try {
if (null !== $parameter->getClass()) {
if (!$this->serializer instanceof DenormalizerInterface) {
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));
}
$parameterClass = $parameter->getClass()->getName();
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createContext($context, $parameterName));

if (null === $this->denormalizer) {
throw new MissingConstructorArgumentsException(sprintf('Could not create object of class "%s" of the parameter "%s".', $parameterClass, $parameterName));
}

$parameterData = $this->denormalizer->denormalize($parameterData, $parameterClass, $format, $context);
}
} catch (\ReflectionException $e) {
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
Expand All @@ -147,5 +116,4 @@ private function denormalizeParameter(\ReflectionClass $class, \ReflectionParame

return $parameterData;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@

namespace Symfony\Component\Serializer\Instantiator;

use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;

/**
* @author Jérôme Desjardins <[email protected]>
*/
interface InstantiatorInterface
{
/**
* Instantiate a new object.
*
* @throws MissingConstructorArgumentsException When some arguments are missing to use the constructor
*
* @return mixed
*/
public function instantiate(string $class, $data, $format = null, array $context = []);

public function createChildContext(string $class, $data, array $context = [], $attribute);

}
}
111 changes: 0 additions & 111 deletions src/Symfony/Component/Serializer/Normalizer/NewObjectNormalizer.php

This file was deleted.

Loading