default services conflit ?
This commit is contained in:
568
old.vendor/symfony/serializer/Normalizer/AbstractNormalizer.php
Normal file
568
old.vendor/symfony/serializer/Normalizer/AbstractNormalizer.php
Normal file
@@ -0,0 +1,568 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
|
||||
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
use Symfony\Component\Serializer\SerializerAwareInterface;
|
||||
use Symfony\Component\Serializer\SerializerAwareTrait;
|
||||
|
||||
/**
|
||||
* Normalizer implementation.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
use ObjectToPopulateTrait;
|
||||
use SerializerAwareTrait;
|
||||
|
||||
/* constants to configure the context */
|
||||
|
||||
/**
|
||||
* How many loops of circular reference to allow while normalizing.
|
||||
*
|
||||
* The default value of 1 means that when we encounter the same object a
|
||||
* second time, we consider that a circular reference.
|
||||
*
|
||||
* You can raise this value for special cases, e.g. in combination with the
|
||||
* max depth setting of the object normalizer.
|
||||
*/
|
||||
public const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit';
|
||||
|
||||
/**
|
||||
* Instead of creating a new instance of an object, update the specified object.
|
||||
*
|
||||
* If you have a nested structure, child objects will be overwritten with
|
||||
* new instances unless you set DEEP_OBJECT_TO_POPULATE to true.
|
||||
*/
|
||||
public const OBJECT_TO_POPULATE = 'object_to_populate';
|
||||
|
||||
/**
|
||||
* Only (de)normalize attributes that are in the specified groups.
|
||||
*/
|
||||
public const GROUPS = 'groups';
|
||||
|
||||
/**
|
||||
* Limit (de)normalize to the specified names.
|
||||
*
|
||||
* For nested structures, this list needs to reflect the object tree.
|
||||
*/
|
||||
public const ATTRIBUTES = 'attributes';
|
||||
|
||||
/**
|
||||
* If ATTRIBUTES are specified, and the source has fields that are not part of that list,
|
||||
* either ignore those attributes (true) or throw an ExtraAttributesException (false).
|
||||
*/
|
||||
public const ALLOW_EXTRA_ATTRIBUTES = 'allow_extra_attributes';
|
||||
|
||||
/**
|
||||
* Hashmap of default values for constructor arguments.
|
||||
*
|
||||
* The names need to match the parameter names in the constructor arguments.
|
||||
*/
|
||||
public const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments';
|
||||
|
||||
/**
|
||||
* Hashmap of field name => callable to normalize this field.
|
||||
*
|
||||
* The callable is called if the field is encountered with the arguments:
|
||||
*
|
||||
* - mixed $attributeValue value of this field
|
||||
* - object $object the whole object being normalized
|
||||
* - string $attributeName name of the attribute being normalized
|
||||
* - string $format the requested format
|
||||
* - array $context the serialization context
|
||||
*/
|
||||
public const CALLBACKS = 'callbacks';
|
||||
|
||||
/**
|
||||
* Handler to call when a circular reference has been detected.
|
||||
*
|
||||
* If you specify no handler, a CircularReferenceException is thrown.
|
||||
*
|
||||
* The method will be called with ($object, $format, $context) and its
|
||||
* return value is returned as the result of the normalize call.
|
||||
*/
|
||||
public const CIRCULAR_REFERENCE_HANDLER = 'circular_reference_handler';
|
||||
|
||||
/**
|
||||
* Skip the specified attributes when normalizing an object tree.
|
||||
*
|
||||
* This list is applied to each element of nested structures.
|
||||
*
|
||||
* Note: The behaviour for nested structures is different from ATTRIBUTES
|
||||
* for historical reason. Aligning the behaviour would be a BC break.
|
||||
*/
|
||||
public const IGNORED_ATTRIBUTES = 'ignored_attributes';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected const CIRCULAR_REFERENCE_LIMIT_COUNTERS = 'circular_reference_limit_counters';
|
||||
|
||||
protected $defaultContext = [
|
||||
self::ALLOW_EXTRA_ATTRIBUTES => true,
|
||||
self::CIRCULAR_REFERENCE_LIMIT => 1,
|
||||
self::IGNORED_ATTRIBUTES => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*/
|
||||
protected $circularReferenceLimit = 1;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @var callable|null
|
||||
*/
|
||||
protected $circularReferenceHandler;
|
||||
|
||||
/**
|
||||
* @var ClassMetadataFactoryInterface|null
|
||||
*/
|
||||
protected $classMetadataFactory;
|
||||
|
||||
/**
|
||||
* @var NameConverterInterface|null
|
||||
*/
|
||||
protected $nameConverter;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*/
|
||||
protected $callbacks = [];
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*/
|
||||
protected $ignoredAttributes = [];
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*/
|
||||
protected $camelizedAttributes = [];
|
||||
|
||||
/**
|
||||
* Sets the {@link ClassMetadataFactoryInterface} to use.
|
||||
*/
|
||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, array $defaultContext = [])
|
||||
{
|
||||
$this->classMetadataFactory = $classMetadataFactory;
|
||||
$this->nameConverter = $nameConverter;
|
||||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
||||
|
||||
if (isset($this->defaultContext[self::CALLBACKS])) {
|
||||
if (!\is_array($this->defaultContext[self::CALLBACKS])) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" default context option must be an array of callables.', self::CALLBACKS));
|
||||
}
|
||||
|
||||
foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) {
|
||||
if (!\is_callable($callback)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" default context option.', $attribute, self::CALLBACKS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER]) && !\is_callable($this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid callback found in the "%s" default context option.', self::CIRCULAR_REFERENCE_HANDLER));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets circular reference limit.
|
||||
*
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @param int $circularReferenceLimit Limit of iterations for the same object
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCircularReferenceLimit($circularReferenceLimit)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "circular_reference_limit" key of the context instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
$this->defaultContext[self::CIRCULAR_REFERENCE_LIMIT] = $this->circularReferenceLimit = $circularReferenceLimit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets circular reference handler.
|
||||
*
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCircularReferenceHandler(callable $circularReferenceHandler)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "circular_reference_handler" key of the context instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
$this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER] = $this->circularReferenceHandler = $circularReferenceHandler;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets normalization callbacks.
|
||||
*
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @param callable[] $callbacks Help normalize the result
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws InvalidArgumentException if a non-callable callback is set
|
||||
*/
|
||||
public function setCallbacks(array $callbacks)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "callbacks" key of the context instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
foreach ($callbacks as $attribute => $callback) {
|
||||
if (!\is_callable($callback)) {
|
||||
throw new InvalidArgumentException(sprintf('The given callback for attribute "%s" is not callable.', $attribute));
|
||||
}
|
||||
}
|
||||
$this->defaultContext[self::CALLBACKS] = $this->callbacks = $callbacks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ignored attributes for normalization and denormalization.
|
||||
*
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIgnoredAttributes(array $ignoredAttributes)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "ignored_attributes" key of the context instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
$this->defaultContext[self::IGNORED_ATTRIBUTES] = $this->ignoredAttributes = $ignoredAttributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if the configured circular reference limit is reached.
|
||||
*
|
||||
* @param object $object
|
||||
* @param array $context
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CircularReferenceException
|
||||
*/
|
||||
protected function isCircularReference($object, &$context)
|
||||
{
|
||||
$objectHash = spl_object_hash($object);
|
||||
|
||||
$circularReferenceLimit = $context[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->circularReferenceLimit;
|
||||
if (isset($context[self::CIRCULAR_REFERENCE_LIMIT_COUNTERS][$objectHash])) {
|
||||
if ($context[self::CIRCULAR_REFERENCE_LIMIT_COUNTERS][$objectHash] >= $circularReferenceLimit) {
|
||||
unset($context[self::CIRCULAR_REFERENCE_LIMIT_COUNTERS][$objectHash]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
++$context[self::CIRCULAR_REFERENCE_LIMIT_COUNTERS][$objectHash];
|
||||
} else {
|
||||
$context[self::CIRCULAR_REFERENCE_LIMIT_COUNTERS][$objectHash] = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a circular reference.
|
||||
*
|
||||
* If a circular reference handler is set, it will be called. Otherwise, a
|
||||
* {@class CircularReferenceException} will be thrown.
|
||||
*
|
||||
* @final since Symfony 4.2
|
||||
*
|
||||
* @param object $object
|
||||
* @param string|null $format
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws CircularReferenceException
|
||||
*/
|
||||
protected function handleCircularReference($object/*, string $format = null, array $context = []*/)
|
||||
{
|
||||
if (\func_num_args() < 2 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface && !$this instanceof \Mockery\MockInterface) {
|
||||
@trigger_error(sprintf('The "%s()" method will have two new "string $format = null" and "array $context = []" arguments in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), \E_USER_DEPRECATED);
|
||||
}
|
||||
$format = \func_num_args() > 1 ? func_get_arg(1) : null;
|
||||
$context = \func_num_args() > 2 ? func_get_arg(2) : [];
|
||||
|
||||
$circularReferenceHandler = $context[self::CIRCULAR_REFERENCE_HANDLER] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER] ?? $this->circularReferenceHandler;
|
||||
if ($circularReferenceHandler) {
|
||||
return $circularReferenceHandler($object, $format, $context);
|
||||
}
|
||||
|
||||
throw new CircularReferenceException(sprintf('A circular reference has been detected when serializing the object of class "%s" (configured limit: %d).', \get_class($object), $context[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->circularReferenceLimit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets attributes to normalize using groups.
|
||||
*
|
||||
* @param string|object $classOrObject
|
||||
* @param bool $attributesAsString If false, return an array of {@link AttributeMetadataInterface}
|
||||
*
|
||||
* @throws LogicException if the 'allow_extra_attributes' context variable is false and no class metadata factory is provided
|
||||
*
|
||||
* @return string[]|AttributeMetadataInterface[]|bool
|
||||
*/
|
||||
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
|
||||
{
|
||||
$allowExtraAttributes = $context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES];
|
||||
if (!$this->classMetadataFactory) {
|
||||
if (!$allowExtraAttributes) {
|
||||
throw new LogicException(sprintf('A class metadata factory must be provided in the constructor when setting "%s" to false.', self::ALLOW_EXTRA_ATTRIBUTES));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmpGroups = $context[self::GROUPS] ?? $this->defaultContext[self::GROUPS] ?? null;
|
||||
$groups = (\is_array($tmpGroups) || is_scalar($tmpGroups)) ? (array) $tmpGroups : false;
|
||||
if (false === $groups && $allowExtraAttributes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowedAttributes = [];
|
||||
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
|
||||
$name = $attributeMetadata->getName();
|
||||
|
||||
if (
|
||||
(false === $groups || array_intersect($attributeMetadata->getGroups(), $groups)) &&
|
||||
$this->isAllowedAttribute($classOrObject, $name, null, $context)
|
||||
) {
|
||||
$allowedAttributes[] = $attributesAsString ? $name : $attributeMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
return $allowedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this attribute allowed?
|
||||
*
|
||||
* @param object|string $classOrObject
|
||||
* @param string $attribute
|
||||
* @param string|null $format
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
$ignoredAttributes = $context[self::IGNORED_ATTRIBUTES] ?? $this->defaultContext[self::IGNORED_ATTRIBUTES] ?? $this->ignoredAttributes;
|
||||
if (\in_array($attribute, $ignoredAttributes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attributes = $context[self::ATTRIBUTES] ?? $this->defaultContext[self::ATTRIBUTES] ?? null;
|
||||
if (isset($attributes[$attribute])) {
|
||||
// Nested attributes
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\is_array($attributes)) {
|
||||
return \in_array($attribute, $attributes, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given data to an array. It's particularly useful during
|
||||
* the denormalization process.
|
||||
*
|
||||
* @param object|array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareForDenormalization($data)
|
||||
{
|
||||
return (array) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method to use to construct an object. This method must be either
|
||||
* the object constructor or static.
|
||||
*
|
||||
* @param string $class
|
||||
* @param array|bool $allowedAttributes
|
||||
*
|
||||
* @return \ReflectionMethod|null
|
||||
*/
|
||||
protected function getConstructor(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
|
||||
{
|
||||
return $reflectionClass->getConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates an object using constructor parameters when needed.
|
||||
*
|
||||
* This method also allows to denormalize data into an existing object if
|
||||
* it is present in the context with the object_to_populate. This object
|
||||
* is removed from the context before being returned to avoid side effects
|
||||
* when recursively normalizing an object graph.
|
||||
*
|
||||
* @param string $class
|
||||
* @param array|bool $allowedAttributes
|
||||
*
|
||||
* @return object
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws MissingConstructorArgumentsException
|
||||
*/
|
||||
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)
|
||||
{
|
||||
if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) {
|
||||
unset($context[self::OBJECT_TO_POPULATE]);
|
||||
|
||||
return $object;
|
||||
}
|
||||
// clean up even if no match
|
||||
unset($context[static::OBJECT_TO_POPULATE]);
|
||||
|
||||
$constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes);
|
||||
if ($constructor) {
|
||||
if (true !== $constructor->isPublic()) {
|
||||
return $reflectionClass->newInstanceWithoutConstructor();
|
||||
}
|
||||
|
||||
$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);
|
||||
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
|
||||
if ($constructorParameter->isVariadic()) {
|
||||
if ($allowed && !$ignored && (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));
|
||||
}
|
||||
|
||||
$variadicParameters = [];
|
||||
foreach ($data[$paramName] as $parameterData) {
|
||||
$variadicParameters[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
|
||||
}
|
||||
|
||||
$params = array_merge($params, $variadicParameters);
|
||||
unset($data[$key]);
|
||||
}
|
||||
} elseif ($allowed && !$ignored && (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[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
|
||||
$params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
|
||||
} elseif (\array_key_exists($key, $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? [])) {
|
||||
$params[] = $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
|
||||
} elseif ($constructorParameter->isDefaultValueAvailable()) {
|
||||
$params[] = $constructorParameter->getDefaultValue();
|
||||
} elseif ($constructorParameter->hasType() && $constructorParameter->getType()->allowsNull()) {
|
||||
$params[] = null;
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
if ($constructor->isConstructor()) {
|
||||
return $reflectionClass->newInstanceArgs($params);
|
||||
} else {
|
||||
return $constructor->invokeArgs(null, $params);
|
||||
}
|
||||
}
|
||||
|
||||
return new $class();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
|
||||
{
|
||||
try {
|
||||
if (($parameterType = $parameter->getType()) instanceof \ReflectionNamedType && !$parameterType->isBuiltin()) {
|
||||
$parameterClass = $parameterType->getName();
|
||||
new \ReflectionClass($parameterClass); // throws a \ReflectionException if the class doesn't exist
|
||||
|
||||
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.', $parameterClass, static::class));
|
||||
}
|
||||
|
||||
return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format));
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
|
||||
} catch (MissingConstructorArgumentsException $e) {
|
||||
if (!$parameter->getType()->allowsNull()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $parameterData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute Attribute name
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function createChildContext(array $parentContext, $attribute/*, ?string $format */): array
|
||||
{
|
||||
if (\func_num_args() < 3) {
|
||||
@trigger_error(sprintf('Method "%s::%s()" will have a third "?string $format" argument in version 5.0; not defining it is deprecated since Symfony 4.3.', static::class, __FUNCTION__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (isset($parentContext[self::ATTRIBUTES][$attribute])) {
|
||||
$parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute];
|
||||
} else {
|
||||
unset($parentContext[self::ATTRIBUTES]);
|
||||
}
|
||||
|
||||
return $parentContext;
|
||||
}
|
||||
}
|
@@ -0,0 +1,640 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
|
||||
/**
|
||||
* Base class for a normalizer dealing with objects.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
{
|
||||
/**
|
||||
* Set to true to respect the max depth metadata on fields.
|
||||
*/
|
||||
public const ENABLE_MAX_DEPTH = 'enable_max_depth';
|
||||
|
||||
/**
|
||||
* How to track the current depth in the context.
|
||||
*/
|
||||
public const DEPTH_KEY_PATTERN = 'depth_%s::%s';
|
||||
|
||||
/**
|
||||
* While denormalizing, we can verify that types match.
|
||||
*
|
||||
* You can disable this by setting this flag to true.
|
||||
*/
|
||||
public const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';
|
||||
|
||||
/**
|
||||
* Flag to control whether fields with the value `null` should be output
|
||||
* when normalizing or omitted.
|
||||
*/
|
||||
public const SKIP_NULL_VALUES = 'skip_null_values';
|
||||
|
||||
/**
|
||||
* Callback to allow to set a value for an attribute when the max depth has
|
||||
* been reached.
|
||||
*
|
||||
* If no callback is given, the attribute is skipped. If a callable is
|
||||
* given, its return value is used (even if null).
|
||||
*
|
||||
* The arguments are:
|
||||
*
|
||||
* - mixed $attributeValue value of this field
|
||||
* - object $object the whole object being normalized
|
||||
* - string $attributeName name of the attribute being normalized
|
||||
* - string $format the requested format
|
||||
* - array $context the serialization context
|
||||
*/
|
||||
public const MAX_DEPTH_HANDLER = 'max_depth_handler';
|
||||
|
||||
/**
|
||||
* Specify which context key are not relevant to determine which attributes
|
||||
* of an object to (de)normalize.
|
||||
*/
|
||||
public const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';
|
||||
|
||||
/**
|
||||
* Flag to tell the denormalizer to also populate existing objects on
|
||||
* attributes of the main object.
|
||||
*
|
||||
* Setting this to true is only useful if you also specify the root object
|
||||
* in OBJECT_TO_POPULATE.
|
||||
*/
|
||||
public const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';
|
||||
|
||||
public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects';
|
||||
|
||||
private $propertyTypeExtractor;
|
||||
private $typesCache = [];
|
||||
private $attributesCache = [];
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.2
|
||||
*
|
||||
* @var callable|null
|
||||
*/
|
||||
private $maxDepthHandler;
|
||||
private $objectClassResolver;
|
||||
|
||||
/**
|
||||
* @var ClassDiscriminatorResolverInterface|null
|
||||
*/
|
||||
protected $classDiscriminatorResolver;
|
||||
|
||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
|
||||
{
|
||||
parent::__construct($classMetadataFactory, $nameConverter, $defaultContext);
|
||||
|
||||
if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER])) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" given in the default context is not callable.', self::MAX_DEPTH_HANDLER));
|
||||
}
|
||||
|
||||
$this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = array_merge($this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] ?? [], [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]);
|
||||
|
||||
$this->propertyTypeExtractor = $propertyTypeExtractor;
|
||||
|
||||
if (null === $classDiscriminatorResolver && null !== $classMetadataFactory) {
|
||||
$classDiscriminatorResolver = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
|
||||
}
|
||||
$this->classDiscriminatorResolver = $classDiscriminatorResolver;
|
||||
$this->objectClassResolver = $objectClassResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return \is_object($data) && !$data instanceof \Traversable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!isset($context['cache_key'])) {
|
||||
$context['cache_key'] = $this->getCacheKey($format, $context);
|
||||
}
|
||||
|
||||
if (isset($context[self::CALLBACKS])) {
|
||||
if (!\is_array($context[self::CALLBACKS])) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" context option must be an array of callables.', self::CALLBACKS));
|
||||
}
|
||||
|
||||
foreach ($context[self::CALLBACKS] as $attribute => $callback) {
|
||||
if (!\is_callable($callback)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" context option.', $attribute, self::CALLBACKS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isCircularReference($object, $context)) {
|
||||
return $this->handleCircularReference($object, $format, $context);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$stack = [];
|
||||
$attributes = $this->getAttributes($object, $format, $context);
|
||||
$class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
||||
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
|
||||
if (isset($context[self::MAX_DEPTH_HANDLER])) {
|
||||
$maxDepthHandler = $context[self::MAX_DEPTH_HANDLER];
|
||||
if (!\is_callable($maxDepthHandler)) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" given in the context is not callable.', self::MAX_DEPTH_HANDLER));
|
||||
}
|
||||
} else {
|
||||
// already validated in constructor resp by type declaration of setMaxDepthHandler
|
||||
$maxDepthHandler = $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler;
|
||||
}
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$maxDepthReached = false;
|
||||
if (null !== $attributesMetadata && ($maxDepthReached = $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) && !$maxDepthHandler) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
|
||||
if ($maxDepthReached) {
|
||||
$attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
$callback = $context[self::CALLBACKS][$attribute] ?? $this->defaultContext[self::CALLBACKS][$attribute] ?? $this->callbacks[$attribute] ?? null;
|
||||
if ($callback) {
|
||||
$attributeValue = $callback($attributeValue, $object, $attribute, $format, $context);
|
||||
}
|
||||
|
||||
if (null !== $attributeValue && !is_scalar($attributeValue)) {
|
||||
$stack[$attribute] = $attributeValue;
|
||||
}
|
||||
|
||||
$data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $context);
|
||||
}
|
||||
|
||||
foreach ($stack as $attribute => $attributeValue) {
|
||||
if (!$this->serializer instanceof NormalizerInterface) {
|
||||
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer.', $attribute));
|
||||
}
|
||||
|
||||
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format)), $class, $format, $context);
|
||||
}
|
||||
|
||||
if (isset($context[self::PRESERVE_EMPTY_OBJECTS]) && !\count($data)) {
|
||||
return new \ArrayObject();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
if ($mappedClass !== $class) {
|
||||
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and caches attributes for the given object, format and context.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string|null $format
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getAttributes($object, $format, array $context)
|
||||
{
|
||||
$class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
||||
$key = $class.'-'.$context['cache_key'];
|
||||
|
||||
if (isset($this->attributesCache[$key])) {
|
||||
return $this->attributesCache[$key];
|
||||
}
|
||||
|
||||
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
|
||||
|
||||
if (false !== $allowedAttributes) {
|
||||
if ($context['cache_key']) {
|
||||
$this->attributesCache[$key] = $allowedAttributes;
|
||||
}
|
||||
|
||||
return $allowedAttributes;
|
||||
}
|
||||
|
||||
$attributes = $this->extractAttributes($object, $format, $context);
|
||||
|
||||
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
|
||||
array_unshift($attributes, $mapping->getTypeProperty());
|
||||
}
|
||||
|
||||
if ($context['cache_key']) {
|
||||
$this->attributesCache[$key] = $attributes;
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts attributes to normalize from the class of the given object, format and context.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string|null $format
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract protected function extractAttributes($object, $format = null, array $context = []);
|
||||
|
||||
/**
|
||||
* Gets the attribute value.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $attribute
|
||||
* @param string|null $format
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function getAttributeValue($object, $attribute, $format = null, array $context = []);
|
||||
|
||||
/**
|
||||
* Sets a handler function that will be called when the max depth is reached.
|
||||
*
|
||||
* @deprecated since Symfony 4.2
|
||||
*/
|
||||
public function setMaxDepthHandler(?callable $handler): void
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "max_depth_handler" key of the context instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
$this->maxDepthHandler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return class_exists($type) || (interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if (!isset($context['cache_key'])) {
|
||||
$context['cache_key'] = $this->getCacheKey($format, $context);
|
||||
}
|
||||
|
||||
$allowedAttributes = $this->getAllowedAttributes($type, $context, true);
|
||||
$normalizedData = $this->prepareForDenormalization($data);
|
||||
$extraAttributes = [];
|
||||
|
||||
$reflectionClass = new \ReflectionClass($type);
|
||||
$object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format);
|
||||
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
||||
|
||||
foreach ($normalizedData as $attribute => $value) {
|
||||
if ($this->nameConverter) {
|
||||
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
|
||||
}
|
||||
|
||||
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) {
|
||||
if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) {
|
||||
$extraAttributes[] = $attribute;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($context[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {
|
||||
try {
|
||||
$context[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $context);
|
||||
} catch (NoSuchPropertyException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$value = $this->validateAndDenormalize($resolvedClass, $attribute, $value, $format, $context);
|
||||
try {
|
||||
$this->setAttributeValue($object, $attribute, $value, $format, $context);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new NotNormalizableValueException(sprintf('Failed to denormalize attribute "%s" value for class "%s": '.$e->getMessage(), $attribute, $type), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($extraAttributes)) {
|
||||
throw new ExtraAttributesException($extraAttributes);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets attribute value.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param string|null $format
|
||||
*/
|
||||
abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = []);
|
||||
|
||||
/**
|
||||
* Validates the submitted data and denormalizes it.
|
||||
*
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws NotNormalizableValueException
|
||||
* @throws LogicException
|
||||
*/
|
||||
private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context)
|
||||
{
|
||||
if (null === $types = $this->getTypes($currentClass, $attribute)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$expectedTypes = [];
|
||||
foreach ($types as $type) {
|
||||
if (null === $data && $type->isNullable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$collectionValueType = $type->isCollection() ? $type->getCollectionValueType() : null;
|
||||
|
||||
// Fix a collection that contains the only one element
|
||||
// This is special to xml format only
|
||||
if ('xml' === $format && null !== $collectionValueType && (!\is_array($data) || !\is_int(key($data)))) {
|
||||
$data = [$data];
|
||||
}
|
||||
|
||||
if (XmlEncoder::FORMAT === $format && '' === $data && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (null !== $collectionValueType && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) {
|
||||
$builtinType = Type::BUILTIN_TYPE_OBJECT;
|
||||
$class = $collectionValueType->getClassName().'[]';
|
||||
|
||||
if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
|
||||
$context['key_type'] = $collectionKeyType;
|
||||
}
|
||||
} elseif ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_ARRAY === $collectionValueType->getBuiltinType()) {
|
||||
// get inner type for any nested array
|
||||
$innerType = $collectionValueType;
|
||||
|
||||
// note that it will break for any other builtinType
|
||||
$dimensions = '[]';
|
||||
while (null !== $innerType->getCollectionValueType() && Type::BUILTIN_TYPE_ARRAY === $innerType->getBuiltinType()) {
|
||||
$dimensions .= '[]';
|
||||
$innerType = $innerType->getCollectionValueType();
|
||||
}
|
||||
|
||||
if (null !== $innerType->getClassName()) {
|
||||
// the builtinType is the inner one and the class is the class followed by []...[]
|
||||
$builtinType = $innerType->getBuiltinType();
|
||||
$class = $innerType->getClassName().$dimensions;
|
||||
} else {
|
||||
// default fallback (keep it as array)
|
||||
$builtinType = $type->getBuiltinType();
|
||||
$class = $type->getClassName();
|
||||
}
|
||||
} else {
|
||||
$builtinType = $type->getBuiltinType();
|
||||
$class = $type->getClassName();
|
||||
}
|
||||
|
||||
$expectedTypes[Type::BUILTIN_TYPE_OBJECT === $builtinType && $class ? $class : $builtinType] = true;
|
||||
|
||||
if (Type::BUILTIN_TYPE_OBJECT === $builtinType) {
|
||||
if (!$this->serializer instanceof DenormalizerInterface) {
|
||||
throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer.', $attribute, $class));
|
||||
}
|
||||
|
||||
$childContext = $this->createChildContext($context, $attribute, $format);
|
||||
if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) {
|
||||
return $this->serializer->denormalize($data, $class, $format, $childContext);
|
||||
}
|
||||
}
|
||||
|
||||
// JSON only has a Number type corresponding to both int and float PHP types.
|
||||
// PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
|
||||
// floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).
|
||||
// PHP's json_decode automatically converts Numbers without a decimal part to integers.
|
||||
// To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
|
||||
// a float is expected.
|
||||
if (Type::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && str_contains($format, JsonEncoder::FORMAT)) {
|
||||
return (float) $data;
|
||||
}
|
||||
|
||||
if (('is_'.$builtinType)($data)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), \gettype($data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
|
||||
{
|
||||
if ($parameter->isVariadic() || null === $this->propertyTypeExtractor || null === $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
|
||||
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
|
||||
}
|
||||
|
||||
return $this->validateAndDenormalize($class->getName(), $parameterName, $parameterData, $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function getTypes(string $currentClass, string $attribute): ?array
|
||||
{
|
||||
if (null === $this->propertyTypeExtractor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$key = $currentClass.'::'.$attribute;
|
||||
if (isset($this->typesCache[$key])) {
|
||||
return false === $this->typesCache[$key] ? null : $this->typesCache[$key];
|
||||
}
|
||||
|
||||
if (null !== $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) {
|
||||
return $this->typesCache[$key] = $types;
|
||||
}
|
||||
|
||||
if (null !== $this->classDiscriminatorResolver && null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($currentClass)) {
|
||||
if ($discriminatorMapping->getTypeProperty() === $attribute) {
|
||||
return $this->typesCache[$key] = [
|
||||
new Type(Type::BUILTIN_TYPE_STRING),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
|
||||
if (null !== $types = $this->propertyTypeExtractor->getTypes($mappedClass, $attribute)) {
|
||||
return $this->typesCache[$key] = $types;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->typesCache[$key] = false;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute and apply the name converter if necessary.
|
||||
*
|
||||
* @param mixed $attributeValue
|
||||
*/
|
||||
private function updateData(array $data, string $attribute, $attributeValue, string $class, ?string $format, array $context): array
|
||||
{
|
||||
if (null === $attributeValue && ($context[self::SKIP_NULL_VALUES] ?? $this->defaultContext[self::SKIP_NULL_VALUES] ?? false)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ($this->nameConverter) {
|
||||
$attribute = $this->nameConverter->normalize($attribute, $class, $format, $context);
|
||||
}
|
||||
|
||||
$data[$attribute] = $attributeValue;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the max depth reached for the given attribute?
|
||||
*
|
||||
* @param AttributeMetadataInterface[] $attributesMetadata
|
||||
*/
|
||||
private function isMaxDepthReached(array $attributesMetadata, string $class, string $attribute, array &$context): bool
|
||||
{
|
||||
$enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? false;
|
||||
if (
|
||||
!$enableMaxDepth ||
|
||||
!isset($attributesMetadata[$attribute]) ||
|
||||
null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = sprintf(self::DEPTH_KEY_PATTERN, $class, $attribute);
|
||||
if (!isset($context[$key])) {
|
||||
$context[$key] = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($context[$key] === $maxDepth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
++$context[$key];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten to update the cache key for the child.
|
||||
*
|
||||
* We must not mix up the attribute cache between parent and children.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $format
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function createChildContext(array $parentContext, $attribute/*, ?string $format */): array
|
||||
{
|
||||
if (\func_num_args() >= 3) {
|
||||
$format = func_get_arg(2);
|
||||
} else {
|
||||
@trigger_error(sprintf('Method "%s::%s()" will have a third "?string $format" argument in version 5.0; not defining it is deprecated since Symfony 4.3.', static::class, __FUNCTION__), \E_USER_DEPRECATED);
|
||||
$format = null;
|
||||
}
|
||||
|
||||
$context = parent::createChildContext($parentContext, $attribute, $format);
|
||||
$context['cache_key'] = $this->getCacheKey($format, $context);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the cache key for the attributes cache.
|
||||
*
|
||||
* The key must be different for every option in the context that could change which attributes should be handled.
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
private function getCacheKey(?string $format, array $context)
|
||||
{
|
||||
foreach ($context[self::EXCLUDE_FROM_CACHE_KEY] ?? $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] as $key) {
|
||||
unset($context[$key]);
|
||||
}
|
||||
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
|
||||
unset($context[self::OBJECT_TO_POPULATE]);
|
||||
unset($context['cache_key']); // avoid artificially different keys
|
||||
|
||||
try {
|
||||
return md5($format.serialize([
|
||||
'context' => $context,
|
||||
'ignored' => $this->ignoredAttributes,
|
||||
'camelized' => $this->camelizedAttributes,
|
||||
]));
|
||||
} catch (\Exception $exception) {
|
||||
// The context cannot be serialized, skip the cache
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
100
old.vendor/symfony/serializer/Normalizer/ArrayDenormalizer.php
Normal file
100
old.vendor/symfony/serializer/Normalizer/ArrayDenormalizer.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\BadMethodCallException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
use Symfony\Component\Serializer\SerializerAwareInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* Denormalizes arrays of objects.
|
||||
*
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class ArrayDenormalizer implements ContextAwareDenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
/**
|
||||
* @var SerializerInterface|DenormalizerInterface
|
||||
*/
|
||||
private $serializer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws NotNormalizableValueException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if (null === $this->serializer) {
|
||||
throw new BadMethodCallException('Please set a serializer before calling denormalize()!');
|
||||
}
|
||||
if (!\is_array($data)) {
|
||||
throw new InvalidArgumentException('Data expected to be an array, '.\gettype($data).' given.');
|
||||
}
|
||||
if (!str_ends_with($type, '[]')) {
|
||||
throw new InvalidArgumentException('Unsupported class: '.$type);
|
||||
}
|
||||
|
||||
$serializer = $this->serializer;
|
||||
$type = substr($type, 0, -2);
|
||||
|
||||
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
|
||||
foreach ($data as $key => $value) {
|
||||
if (null !== $builtinType && !('is_'.$builtinType)($key)) {
|
||||
throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, \gettype($key)));
|
||||
}
|
||||
|
||||
$data[$key] = $serializer->denormalize($value, $type, $format, $context);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
|
||||
{
|
||||
if (null === $this->serializer) {
|
||||
throw new BadMethodCallException(sprintf('The serializer needs to be set to allow "%s()" to be used.', __METHOD__));
|
||||
}
|
||||
|
||||
return str_ends_with($type, '[]')
|
||||
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSerializer(SerializerInterface $serializer)
|
||||
{
|
||||
if (!$serializer instanceof DenormalizerInterface) {
|
||||
throw new InvalidArgumentException('Expected a serializer that also implements DenormalizerInterface.');
|
||||
}
|
||||
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return $this->serializer instanceof CacheableSupportsMethodInterface && $this->serializer->hasCacheableSupportsMethod();
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Marker interface for normalizers and denormalizers that use
|
||||
* only the type and the format in their supports*() methods.
|
||||
*
|
||||
* By implementing this interface, the return value of the
|
||||
* supports*() methods will be cached by type and format.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface CacheableSupportsMethodInterface
|
||||
{
|
||||
public function hasCacheableSupportsMethod(): bool;
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
|
||||
/**
|
||||
* A normalizer that normalizes a ConstraintViolationListInterface instance.
|
||||
*
|
||||
* This Normalizer implements RFC7807 {@link https://tools.ietf.org/html/rfc7807}.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ConstraintViolationListNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
public const INSTANCE = 'instance';
|
||||
public const STATUS = 'status';
|
||||
public const TITLE = 'title';
|
||||
public const TYPE = 'type';
|
||||
|
||||
private $defaultContext;
|
||||
private $nameConverter;
|
||||
|
||||
public function __construct(array $defaultContext = [], NameConverterInterface $nameConverter = null)
|
||||
{
|
||||
$this->defaultContext = $defaultContext;
|
||||
$this->nameConverter = $nameConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
$violations = [];
|
||||
$messages = [];
|
||||
foreach ($object as $violation) {
|
||||
$propertyPath = $this->nameConverter ? $this->nameConverter->normalize($violation->getPropertyPath(), null, $format, $context) : $violation->getPropertyPath();
|
||||
|
||||
$violationEntry = [
|
||||
'propertyPath' => $propertyPath,
|
||||
'title' => $violation->getMessage(),
|
||||
'parameters' => $violation->getParameters(),
|
||||
];
|
||||
if (null !== $code = $violation->getCode()) {
|
||||
$violationEntry['type'] = sprintf('urn:uuid:%s', $code);
|
||||
}
|
||||
|
||||
$violations[] = $violationEntry;
|
||||
|
||||
$prefix = $propertyPath ? sprintf('%s: ', $propertyPath) : '';
|
||||
$messages[] = $prefix.$violation->getMessage();
|
||||
}
|
||||
|
||||
$result = [
|
||||
'type' => $context[self::TYPE] ?? $this->defaultContext[self::TYPE] ?? 'https://symfony.com/errors/validation',
|
||||
'title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE] ?? 'Validation Failed',
|
||||
];
|
||||
if (null !== $status = ($context[self::STATUS] ?? $this->defaultContext[self::STATUS] ?? null)) {
|
||||
$result['status'] = $status;
|
||||
}
|
||||
if ($messages) {
|
||||
$result['detail'] = implode("\n", $messages);
|
||||
}
|
||||
if (null !== $instance = ($context[self::INSTANCE] ?? $this->defaultContext[self::INSTANCE] ?? null)) {
|
||||
$result['instance'] = $instance;
|
||||
}
|
||||
|
||||
return $result + ['violations' => $violations];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof ConstraintViolationListInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Adds the support of an extra $context parameter for the supportsDenormalization method.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface ContextAwareDenormalizerInterface extends DenormalizerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $context options that denormalizers have access to
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null, array $context = []);
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Adds the support of an extra $context parameter for the supportsNormalization method.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface ContextAwareNormalizerInterface extends NormalizerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $context options that normalizers have access to
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null, array $context = []);
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\SerializerAwareInterface;
|
||||
use Symfony\Component\Serializer\SerializerAwareTrait;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
use ObjectToPopulateTrait;
|
||||
use SerializerAwareTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
return $object->normalize($this->serializer, $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
$object = $this->extractObjectToPopulate($type, $context) ?? new $type();
|
||||
$object->denormalize($this->serializer, $data, $format, $context);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given class implements the NormalizableInterface.
|
||||
*
|
||||
* @param mixed $data Data to normalize
|
||||
* @param string $format The format being (de-)serialized from or into
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof NormalizableInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given class implements the DenormalizableInterface.
|
||||
*
|
||||
* @param mixed $data Data to denormalize from
|
||||
* @param string $type The class to which the data should be denormalized
|
||||
* @param string $format The format being deserialized from
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return is_subclass_of($type, DenormalizableInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
}
|
179
old.vendor/symfony/serializer/Normalizer/DataUriNormalizer.php
Normal file
179
old.vendor/symfony/serializer/Normalizer/DataUriNormalizer.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface as DeprecatedMimeTypeGuesserInterface;
|
||||
use Symfony\Component\Mime\MimeTypeGuesserInterface;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
|
||||
/**
|
||||
* Normalizes an {@see \SplFileInfo} object to a data URI.
|
||||
* Denormalizes a data URI to a {@see \SplFileObject} object.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
private const SUPPORTED_TYPES = [
|
||||
\SplFileInfo::class => true,
|
||||
\SplFileObject::class => true,
|
||||
File::class => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var MimeTypeGuesserInterface|null
|
||||
*/
|
||||
private $mimeTypeGuesser;
|
||||
|
||||
/**
|
||||
* @param MimeTypeGuesserInterface|null $mimeTypeGuesser
|
||||
*/
|
||||
public function __construct($mimeTypeGuesser = null)
|
||||
{
|
||||
if ($mimeTypeGuesser instanceof DeprecatedMimeTypeGuesserInterface) {
|
||||
@trigger_error(sprintf('Passing a %s to "%s()" is deprecated since Symfony 4.3, pass a "%s" instead.', DeprecatedMimeTypeGuesserInterface::class, __METHOD__, MimeTypeGuesserInterface::class), \E_USER_DEPRECATED);
|
||||
} elseif (null === $mimeTypeGuesser) {
|
||||
if (class_exists(MimeTypes::class)) {
|
||||
$mimeTypeGuesser = MimeTypes::getDefault();
|
||||
} elseif (class_exists(MimeTypeGuesser::class)) {
|
||||
@trigger_error(sprintf('Passing null to "%s()" to use a default MIME type guesser without Symfony Mime installed is deprecated since Symfony 4.3. Try running "composer require symfony/mime".', __METHOD__), \E_USER_DEPRECATED);
|
||||
$mimeTypeGuesser = MimeTypeGuesser::getInstance();
|
||||
}
|
||||
} elseif (!$mimeTypeGuesser instanceof MimeTypes) {
|
||||
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an instance of "%s" or null, "%s" given.', __METHOD__, MimeTypes::class, \is_object($mimeTypeGuesser) ? \get_class($mimeTypeGuesser) : \gettype($mimeTypeGuesser)));
|
||||
}
|
||||
|
||||
$this->mimeTypeGuesser = $mimeTypeGuesser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!$object instanceof \SplFileInfo) {
|
||||
throw new InvalidArgumentException('The object must be an instance of "\SplFileInfo".');
|
||||
}
|
||||
|
||||
$mimeType = $this->getMimeType($object);
|
||||
$splFileObject = $this->extractSplFileObject($object);
|
||||
|
||||
$data = '';
|
||||
|
||||
$splFileObject->rewind();
|
||||
while (!$splFileObject->eof()) {
|
||||
$data .= $splFileObject->fgets();
|
||||
}
|
||||
|
||||
if ('text' === explode('/', $mimeType, 2)[0]) {
|
||||
return sprintf('data:%s,%s', $mimeType, rawurlencode($data));
|
||||
}
|
||||
|
||||
return sprintf('data:%s;base64,%s', $mimeType, base64_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof \SplFileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Regex adapted from Brian Grinstead code.
|
||||
*
|
||||
* @see https://gist.github.com/bgrins/6194623
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NotNormalizableValueException
|
||||
*
|
||||
* @return \SplFileInfo
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if (null === $data || !preg_match('/^data:([a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}\/[a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}(;[a-z0-9\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\\\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i', $data)) {
|
||||
throw new NotNormalizableValueException('The provided "data:" URI is not valid.');
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
case File::class:
|
||||
return new File($data, false);
|
||||
|
||||
case 'SplFileObject':
|
||||
case 'SplFileInfo':
|
||||
return new \SplFileObject($data);
|
||||
}
|
||||
} catch (\RuntimeException $exception) {
|
||||
throw new NotNormalizableValueException($exception->getMessage(), $exception->getCode(), $exception);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The class parameter "%s" is not supported. It must be one of "SplFileInfo", "SplFileObject" or "Symfony\Component\HttpFoundation\File\File".', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return isset(self::SUPPORTED_TYPES[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mime type of the object. Defaults to application/octet-stream.
|
||||
*/
|
||||
private function getMimeType(\SplFileInfo $object): string
|
||||
{
|
||||
if ($object instanceof File) {
|
||||
return $object->getMimeType();
|
||||
}
|
||||
|
||||
if ($this->mimeTypeGuesser instanceof DeprecatedMimeTypeGuesserInterface && $mimeType = $this->mimeTypeGuesser->guess($object->getPathname())) {
|
||||
return $mimeType;
|
||||
}
|
||||
|
||||
if ($this->mimeTypeGuesser && $mimeType = $this->mimeTypeGuesser->guessMimeType($object->getPathname())) {
|
||||
return $mimeType;
|
||||
}
|
||||
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the \SplFileObject instance associated with the given \SplFileInfo instance.
|
||||
*/
|
||||
private function extractSplFileObject(\SplFileInfo $object): \SplFileObject
|
||||
{
|
||||
if ($object instanceof \SplFileObject) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
return $object->openFile();
|
||||
}
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Normalizes an instance of {@see \DateInterval} to an interval string.
|
||||
* Denormalizes an interval string to an instance of {@see \DateInterval}.
|
||||
*
|
||||
* @author Jérôme Parmentier <jerome@prmntr.me>
|
||||
*/
|
||||
class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
public const FORMAT_KEY = 'dateinterval_format';
|
||||
|
||||
private $defaultContext = [
|
||||
self::FORMAT_KEY => '%rP%yY%mM%dDT%hH%iM%sS',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array $defaultContext
|
||||
*/
|
||||
public function __construct($defaultContext = [])
|
||||
{
|
||||
if (!\is_array($defaultContext)) {
|
||||
@trigger_error(sprintf('The "format" parameter is deprecated since Symfony 4.2, use the "%s" key of the context instead.', self::FORMAT_KEY), \E_USER_DEPRECATED);
|
||||
|
||||
$defaultContext = [self::FORMAT_KEY => (string) $defaultContext];
|
||||
}
|
||||
|
||||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!$object instanceof \DateInterval) {
|
||||
throw new InvalidArgumentException('The object must be an instance of "\DateInterval".');
|
||||
}
|
||||
|
||||
return $object->format($context[self::FORMAT_KEY] ?? $this->defaultContext[self::FORMAT_KEY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof \DateInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
*
|
||||
* @return \DateInterval
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if (!\is_string($data)) {
|
||||
throw new InvalidArgumentException(sprintf('Data expected to be a string, "%s" given.', \gettype($data)));
|
||||
}
|
||||
|
||||
if (!$this->isISO8601($data)) {
|
||||
throw new UnexpectedValueException('Expected a valid ISO 8601 interval string.');
|
||||
}
|
||||
|
||||
$dateIntervalFormat = $context[self::FORMAT_KEY] ?? $this->defaultContext[self::FORMAT_KEY];
|
||||
|
||||
$signPattern = '';
|
||||
switch (substr($dateIntervalFormat, 0, 2)) {
|
||||
case '%R':
|
||||
$signPattern = '[-+]';
|
||||
$dateIntervalFormat = substr($dateIntervalFormat, 2);
|
||||
break;
|
||||
case '%r':
|
||||
$signPattern = '-?';
|
||||
$dateIntervalFormat = substr($dateIntervalFormat, 2);
|
||||
break;
|
||||
}
|
||||
$valuePattern = '/^'.$signPattern.preg_replace('/%([yYmMdDhHiIsSwW])(\w)/', '(?:(?P<$1>\d+)$2)?', preg_replace('/(T.*)$/', '($1)?', $dateIntervalFormat)).'$/';
|
||||
if (!preg_match($valuePattern, $data)) {
|
||||
throw new UnexpectedValueException(sprintf('Value "%s" contains intervals not accepted by format "%s".', $data, $dateIntervalFormat));
|
||||
}
|
||||
|
||||
try {
|
||||
if ('-' === $data[0]) {
|
||||
$interval = new \DateInterval(substr($data, 1));
|
||||
$interval->invert = 1;
|
||||
|
||||
return $interval;
|
||||
}
|
||||
|
||||
if ('+' === $data[0]) {
|
||||
return new \DateInterval(substr($data, 1));
|
||||
}
|
||||
|
||||
return new \DateInterval($data);
|
||||
} catch (\Exception $e) {
|
||||
throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return \DateInterval::class === $type;
|
||||
}
|
||||
|
||||
private function isISO8601(string $string): bool
|
||||
{
|
||||
return preg_match('/^[\-+]?P(?=\w*(?:\d|%\w))(?:\d+Y|%[yY]Y)?(?:\d+M|%[mM]M)?(?:(?:\d+D|%[dD]D)|(?:\d+W|%[wW]W))?(?:T(?:\d+H|[hH]H)?(?:\d+M|[iI]M)?(?:\d+S|[sS]S)?)?$/', $string);
|
||||
}
|
||||
}
|
165
old.vendor/symfony/serializer/Normalizer/DateTimeNormalizer.php
Normal file
165
old.vendor/symfony/serializer/Normalizer/DateTimeNormalizer.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
|
||||
/**
|
||||
* Normalizes an object implementing the {@see \DateTimeInterface} to a date string.
|
||||
* Denormalizes a date string to an instance of {@see \DateTime} or {@see \DateTimeImmutable}.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
public const FORMAT_KEY = 'datetime_format';
|
||||
public const TIMEZONE_KEY = 'datetime_timezone';
|
||||
|
||||
private $defaultContext;
|
||||
|
||||
private const SUPPORTED_TYPES = [
|
||||
\DateTimeInterface::class => true,
|
||||
\DateTimeImmutable::class => true,
|
||||
\DateTime::class => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array $defaultContext
|
||||
*/
|
||||
public function __construct($defaultContext = [], \DateTimeZone $timezone = null)
|
||||
{
|
||||
$this->defaultContext = [
|
||||
self::FORMAT_KEY => \DateTime::RFC3339,
|
||||
self::TIMEZONE_KEY => null,
|
||||
];
|
||||
|
||||
if (!\is_array($defaultContext)) {
|
||||
@trigger_error('Passing configuration options directly to the constructor is deprecated since Symfony 4.2, use the default context instead.', \E_USER_DEPRECATED);
|
||||
|
||||
$defaultContext = [self::FORMAT_KEY => (string) $defaultContext];
|
||||
$defaultContext[self::TIMEZONE_KEY] = $timezone;
|
||||
}
|
||||
|
||||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!$object instanceof \DateTimeInterface) {
|
||||
throw new InvalidArgumentException('The object must implement the "\DateTimeInterface".');
|
||||
}
|
||||
|
||||
$dateTimeFormat = $context[self::FORMAT_KEY] ?? $this->defaultContext[self::FORMAT_KEY];
|
||||
$timezone = $this->getTimezone($context);
|
||||
|
||||
if (null !== $timezone) {
|
||||
$object = clone $object;
|
||||
$object = $object->setTimezone($timezone);
|
||||
}
|
||||
|
||||
return $object->format($dateTimeFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof \DateTimeInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws NotNormalizableValueException
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
$dateTimeFormat = $context[self::FORMAT_KEY] ?? null;
|
||||
$timezone = $this->getTimezone($context);
|
||||
|
||||
if (null === $data || (\is_string($data) && '' === trim($data))) {
|
||||
throw new NotNormalizableValueException('The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.');
|
||||
}
|
||||
|
||||
if (null !== $dateTimeFormat) {
|
||||
$object = \DateTime::class === $type ? \DateTime::createFromFormat($dateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data, $timezone);
|
||||
|
||||
if (false !== $object) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
$dateTimeErrors = \DateTime::class === $type ? \DateTime::getLastErrors() : \DateTimeImmutable::getLastErrors();
|
||||
|
||||
throw new NotNormalizableValueException(sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $data, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors'])));
|
||||
}
|
||||
|
||||
try {
|
||||
return \DateTime::class === $type ? new \DateTime($data, $timezone) : new \DateTimeImmutable($data, $timezone);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotNormalizableValueException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return isset(self::SUPPORTED_TYPES[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats datetime errors.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function formatDateTimeErrors(array $errors): array
|
||||
{
|
||||
$formattedErrors = [];
|
||||
|
||||
foreach ($errors as $pos => $message) {
|
||||
$formattedErrors[] = sprintf('at position %d: %s', $pos, $message);
|
||||
}
|
||||
|
||||
return $formattedErrors;
|
||||
}
|
||||
|
||||
private function getTimezone(array $context): ?\DateTimeZone
|
||||
{
|
||||
$dateTimeZone = $context[self::TIMEZONE_KEY] ?? $this->defaultContext[self::TIMEZONE_KEY];
|
||||
|
||||
if (null === $dateTimeZone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $dateTimeZone instanceof \DateTimeZone ? $dateTimeZone : new \DateTimeZone($dateTimeZone);
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
|
||||
/**
|
||||
* Normalizes a {@see \DateTimeZone} object to a timezone string.
|
||||
*
|
||||
* @author Jérôme Desjardins <jewome62@gmail.com>
|
||||
*/
|
||||
class DateTimeZoneNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!$object instanceof \DateTimeZone) {
|
||||
throw new InvalidArgumentException('The object must be an instance of "\DateTimeZone".');
|
||||
}
|
||||
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof \DateTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws NotNormalizableValueException
|
||||
*
|
||||
* @return \DateTimeZone
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if ('' === $data || null === $data) {
|
||||
throw new NotNormalizableValueException('The data is either an empty string or null, you should pass a string that can be parsed as a DateTimeZone.');
|
||||
}
|
||||
|
||||
try {
|
||||
return new \DateTimeZone($data);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotNormalizableValueException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return \DateTimeZone::class === $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Defines the most basic interface a class must implement to be denormalizable.
|
||||
*
|
||||
* If a denormalizer is registered for the class and it doesn't implement
|
||||
* the Denormalizable interfaces, the normalizer will be used instead
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
interface DenormalizableInterface
|
||||
{
|
||||
/**
|
||||
* Denormalizes the object back from an array of scalars|arrays.
|
||||
*
|
||||
* It is important to understand that the denormalize() call should denormalize
|
||||
* recursively all child objects of the implementor.
|
||||
*
|
||||
* @param DenormalizerInterface $denormalizer The denormalizer is given so that you
|
||||
* can use it to denormalize objects contained within this object
|
||||
* @param array|string|int|float|bool $data The data from which to re-create the object
|
||||
* @param string|null $format The format is optionally given to be able to denormalize
|
||||
* differently based on different input formats
|
||||
* @param array $context Options for denormalizing
|
||||
*/
|
||||
public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null, array $context = []);
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
interface DenormalizerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Sets the owning Denormalizer object.
|
||||
*/
|
||||
public function setDenormalizer(DenormalizerInterface $denormalizer);
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
trait DenormalizerAwareTrait
|
||||
{
|
||||
/**
|
||||
* @var DenormalizerInterface
|
||||
*/
|
||||
protected $denormalizer;
|
||||
|
||||
public function setDenormalizer(DenormalizerInterface $denormalizer)
|
||||
{
|
||||
$this->denormalizer = $denormalizer;
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\BadMethodCallException;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
interface DenormalizerInterface
|
||||
{
|
||||
/**
|
||||
* Denormalizes data back into an object of the given class.
|
||||
*
|
||||
* @param mixed $data Data to restore
|
||||
* @param string $type The expected class to instantiate
|
||||
* @param string $format Format the given data was extracted from
|
||||
* @param array $context Options available to the denormalizer
|
||||
*
|
||||
* @return object|array
|
||||
*
|
||||
* @throws BadMethodCallException Occurs when the normalizer is not called in an expected context
|
||||
* @throws InvalidArgumentException Occurs when the arguments are not coherent or not supported
|
||||
* @throws UnexpectedValueException Occurs when the item cannot be hydrated with the given data
|
||||
* @throws ExtraAttributesException Occurs when the item doesn't have attribute to receive given data
|
||||
* @throws LogicException Occurs when the normalizer is not supposed to denormalize
|
||||
* @throws RuntimeException Occurs if the class cannot be instantiated
|
||||
* @throws ExceptionInterface Occurs for all the other cases of errors
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = []);
|
||||
|
||||
/**
|
||||
* Checks whether the given class is supported for denormalization by this normalizer.
|
||||
*
|
||||
* @param mixed $data Data to denormalize from
|
||||
* @param string $type The class to which the data should be denormalized
|
||||
* @param string $format The format being deserialized from
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null);
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Converts between objects with getter and setter methods and arrays.
|
||||
*
|
||||
* The normalization process looks at all public methods and calls the ones
|
||||
* which have a name starting with get and take no parameters. The result is a
|
||||
* map from property names (method name stripped of the get prefix and converted
|
||||
* to lower case) to property values. Property values are normalized through the
|
||||
* serializer.
|
||||
*
|
||||
* The denormalization first looks at the constructor of the given class to see
|
||||
* if any of the parameters have the same name as one of the properties. The
|
||||
* constructor is then called with all parameters or an exception is thrown if
|
||||
* any required parameters were not present as properties. Then the denormalizer
|
||||
* walks through the given map of property names to property values to see if a
|
||||
* setter method exists for any of the properties. If a setter exists it is
|
||||
* called with the property value. No automatic denormalization of the value
|
||||
* takes place.
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class GetSetMethodNormalizer extends AbstractObjectNormalizer
|
||||
{
|
||||
private static $setterAccessibleCache = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given class has any getter method.
|
||||
*/
|
||||
private function supports(string $class): bool
|
||||
{
|
||||
$class = new \ReflectionClass($class);
|
||||
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $method) {
|
||||
if ($this->isGetMethod($method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a method's name matches /^(get|is|has).+$/ and can be called non-statically without parameters.
|
||||
*/
|
||||
private function isGetMethod(\ReflectionMethod $method): bool
|
||||
{
|
||||
$methodLength = \strlen($method->name);
|
||||
|
||||
return
|
||||
!$method->isStatic() &&
|
||||
(
|
||||
((str_starts_with($method->name, 'get') && 3 < $methodLength) ||
|
||||
(str_starts_with($method->name, 'is') && 2 < $methodLength) ||
|
||||
(str_starts_with($method->name, 'has') && 3 < $methodLength)) &&
|
||||
0 === $method->getNumberOfRequiredParameters()
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function extractAttributes($object, $format = null, array $context = [])
|
||||
{
|
||||
$reflectionObject = new \ReflectionObject($object);
|
||||
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
|
||||
$attributes = [];
|
||||
foreach ($reflectionMethods as $method) {
|
||||
if (!$this->isGetMethod($method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributeName = lcfirst(substr($method->name, str_starts_with($method->name, 'is') ? 2 : 3));
|
||||
|
||||
if ($this->isAllowedAttribute($object, $attributeName, $format, $context)) {
|
||||
$attributes[] = $attributeName;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
$ucfirsted = ucfirst($attribute);
|
||||
|
||||
$getter = 'get'.$ucfirsted;
|
||||
if (\is_callable([$object, $getter])) {
|
||||
return $object->$getter();
|
||||
}
|
||||
|
||||
$isser = 'is'.$ucfirsted;
|
||||
if (\is_callable([$object, $isser])) {
|
||||
return $object->$isser();
|
||||
}
|
||||
|
||||
$haser = 'has'.$ucfirsted;
|
||||
if (\is_callable([$object, $haser])) {
|
||||
return $object->$haser();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
|
||||
{
|
||||
$setter = 'set'.ucfirst($attribute);
|
||||
$key = \get_class($object).':'.$setter;
|
||||
|
||||
if (!isset(self::$setterAccessibleCache[$key])) {
|
||||
self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic();
|
||||
}
|
||||
|
||||
if (self::$setterAccessibleCache[$key]) {
|
||||
$object->$setter($value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
|
||||
/**
|
||||
* A normalizer that uses an objects own JsonSerializable implementation.
|
||||
*
|
||||
* @author Fred Cox <mcfedr@gmail.com>
|
||||
*/
|
||||
class JsonSerializableNormalizer extends AbstractNormalizer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if ($this->isCircularReference($object, $context)) {
|
||||
return $this->handleCircularReference($object);
|
||||
}
|
||||
|
||||
if (!$object instanceof \JsonSerializable) {
|
||||
throw new InvalidArgumentException(sprintf('The object must implement "%s".', \JsonSerializable::class));
|
||||
}
|
||||
|
||||
if (!$this->serializer instanceof NormalizerInterface) {
|
||||
throw new LogicException('Cannot normalize object because injected serializer is not a normalizer.');
|
||||
}
|
||||
|
||||
return $this->serializer->normalize($object->jsonSerialize(), $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return $data instanceof \JsonSerializable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
throw new LogicException(sprintf('Cannot denormalize with "%s".', \JsonSerializable::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Defines the most basic interface a class must implement to be normalizable.
|
||||
*
|
||||
* If a normalizer is registered for the class and it doesn't implement
|
||||
* the Normalizable interfaces, the normalizer will be used instead.
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
interface NormalizableInterface
|
||||
{
|
||||
/**
|
||||
* Normalizes the object into an array of scalars|arrays.
|
||||
*
|
||||
* It is important to understand that the normalize() call should normalize
|
||||
* recursively all child objects of the implementor.
|
||||
*
|
||||
* @param NormalizerInterface $normalizer The normalizer is given so that you
|
||||
* can use it to normalize objects contained within this object
|
||||
* @param string|null $format The format is optionally given to be able to normalize differently
|
||||
* based on different output formats
|
||||
* @param array $context Options for normalizing this object
|
||||
*
|
||||
* @return array|string|int|float|bool
|
||||
*/
|
||||
public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []);
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
interface NormalizerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Sets the owning Normalizer object.
|
||||
*/
|
||||
public function setNormalizer(NormalizerInterface $normalizer);
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
trait NormalizerAwareTrait
|
||||
{
|
||||
/**
|
||||
* @var NormalizerInterface
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
public function setNormalizer(NormalizerInterface $normalizer)
|
||||
{
|
||||
$this->normalizer = $normalizer;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
interface NormalizerInterface
|
||||
{
|
||||
/**
|
||||
* Normalizes an object into a set of arrays/scalars.
|
||||
*
|
||||
* @param mixed $object Object to normalize
|
||||
* @param string $format Format the normalization result will be encoded as
|
||||
* @param array $context Context options for the normalizer
|
||||
*
|
||||
* @return array|string|int|float|bool|\ArrayObject|null \ArrayObject is used to make sure an empty object is encoded as an object not an array
|
||||
*
|
||||
* @throws InvalidArgumentException Occurs when the object given is not a supported type for the normalizer
|
||||
* @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular
|
||||
* reference handler can fix it
|
||||
* @throws LogicException Occurs when the normalizer is not called in an expected context
|
||||
* @throws ExceptionInterface Occurs for all the other cases of errors
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = []);
|
||||
|
||||
/**
|
||||
* Checks whether the given class is supported for normalization by this normalizer.
|
||||
*
|
||||
* @param mixed $data Data to normalize
|
||||
* @param string $format The format being (de-)serialized from or into
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null);
|
||||
}
|
183
old.vendor/symfony/serializer/Normalizer/ObjectNormalizer.php
Normal file
183
old.vendor/symfony/serializer/Normalizer/ObjectNormalizer.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
|
||||
/**
|
||||
* Converts between objects and arrays using the PropertyAccess component.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ObjectNormalizer extends AbstractObjectNormalizer
|
||||
{
|
||||
protected $propertyAccessor;
|
||||
|
||||
private $discriminatorCache = [];
|
||||
|
||||
private $objectClassResolver;
|
||||
|
||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
|
||||
{
|
||||
if (!class_exists(PropertyAccess::class)) {
|
||||
throw new LogicException('The ObjectNormalizer class requires the "PropertyAccess" component. Install "symfony/property-access" to use it.');
|
||||
}
|
||||
|
||||
parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);
|
||||
|
||||
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
|
||||
|
||||
$this->objectClassResolver = $objectClassResolver ?? function ($class) {
|
||||
return \is_object($class) ? \get_class($class) : $class;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function extractAttributes($object, $format = null, array $context = [])
|
||||
{
|
||||
// If not using groups, detect manually
|
||||
$attributes = [];
|
||||
|
||||
// methods
|
||||
$class = ($this->objectClassResolver)($object);
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
|
||||
foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) {
|
||||
if (
|
||||
0 !== $reflMethod->getNumberOfRequiredParameters() ||
|
||||
$reflMethod->isStatic() ||
|
||||
$reflMethod->isConstructor() ||
|
||||
$reflMethod->isDestructor()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $reflMethod->name;
|
||||
$attributeName = null;
|
||||
|
||||
if (str_starts_with($name, 'get') || str_starts_with($name, 'has')) {
|
||||
// getters and hassers
|
||||
$attributeName = substr($name, 3);
|
||||
|
||||
if (!$reflClass->hasProperty($attributeName)) {
|
||||
$attributeName = lcfirst($attributeName);
|
||||
}
|
||||
} elseif (str_starts_with($name, 'is')) {
|
||||
// issers
|
||||
$attributeName = substr($name, 2);
|
||||
|
||||
if (!$reflClass->hasProperty($attributeName)) {
|
||||
$attributeName = lcfirst($attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $attributeName && $this->isAllowedAttribute($object, $attributeName, $format, $context)) {
|
||||
$attributes[$attributeName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// properties
|
||||
$propertyValues = !method_exists($object, '__get') ? (array) $object : null;
|
||||
foreach ($reflClass->getProperties() as $reflProperty) {
|
||||
if (null !== $propertyValues && !\array_key_exists($reflProperty->name, $propertyValues)) {
|
||||
if ($reflProperty->isPublic()
|
||||
|| ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues))
|
||||
|| ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues))
|
||||
) {
|
||||
unset($attributes[$reflProperty->name]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($reflProperty->isStatic() || !$this->isAllowedAttribute($object, $reflProperty->name, $format, $context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[$reflProperty->name] = true;
|
||||
}
|
||||
|
||||
return array_keys($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
$cacheKey = \get_class($object);
|
||||
if (!\array_key_exists($cacheKey, $this->discriminatorCache)) {
|
||||
$this->discriminatorCache[$cacheKey] = null;
|
||||
if (null !== $this->classDiscriminatorResolver) {
|
||||
$mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object);
|
||||
$this->discriminatorCache[$cacheKey] = null === $mapping ? null : $mapping->getTypeProperty();
|
||||
}
|
||||
}
|
||||
|
||||
return $attribute === $this->discriminatorCache[$cacheKey] ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) : $this->propertyAccessor->getValue($object, $attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
|
||||
{
|
||||
try {
|
||||
$this->propertyAccessor->setValue($object, $attribute, $value);
|
||||
} catch (NoSuchPropertyException $exception) {
|
||||
// Properties not found are ignored
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
|
||||
{
|
||||
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $this->classDiscriminatorResolver) {
|
||||
$class = \is_object($classOrObject) ? \get_class($classOrObject) : $classOrObject;
|
||||
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
|
||||
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
|
||||
}
|
||||
|
||||
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
|
||||
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
|
||||
$allowedAttributes = array_merge($allowedAttributes, parent::getAllowedAttributes($mappedClass, $context, $attributesAsString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $allowedAttributes;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
trait ObjectToPopulateTrait
|
||||
{
|
||||
/**
|
||||
* Extract the `object_to_populate` field from the context if it exists
|
||||
* and is an instance of the provided $class.
|
||||
*
|
||||
* @param string $class The class the object should be
|
||||
* @param string|null $key They in which to look for the object to populate.
|
||||
* Keeps backwards compatibility with `AbstractNormalizer`.
|
||||
*
|
||||
* @return object|null an object if things check out, null otherwise
|
||||
*/
|
||||
protected function extractObjectToPopulate($class, array $context, $key = null)
|
||||
{
|
||||
$key = $key ?? AbstractNormalizer::OBJECT_TO_POPULATE;
|
||||
|
||||
if (isset($context[$key]) && \is_object($context[$key]) && $context[$key] instanceof $class) {
|
||||
return $context[$key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
use Symfony\Component\ErrorHandler\Exception\FlattenException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Normalizes errors according to the API Problem spec (RFC 7807).
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7807
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class ProblemNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
private $debug;
|
||||
private $defaultContext = [
|
||||
'type' => 'https://tools.ietf.org/html/rfc2616#section-10',
|
||||
'title' => 'An error occurred',
|
||||
];
|
||||
|
||||
public function __construct(bool $debug = false, array $defaultContext = [])
|
||||
{
|
||||
$this->debug = $debug;
|
||||
$this->defaultContext = $defaultContext + $this->defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function normalize($object, $format = null, array $context = [])
|
||||
{
|
||||
if (!$object instanceof FlattenException) {
|
||||
throw new InvalidArgumentException(sprintf('The object must implement "%s".', FlattenException::class));
|
||||
}
|
||||
|
||||
$context += $this->defaultContext;
|
||||
$debug = $this->debug && ($context['debug'] ?? true);
|
||||
|
||||
$data = [
|
||||
'type' => $context['type'],
|
||||
'title' => $context['title'],
|
||||
'status' => $context['status'] ?? $object->getStatusCode(),
|
||||
'detail' => $debug ? $object->getMessage() : $object->getStatusText(),
|
||||
];
|
||||
if ($debug) {
|
||||
$data['class'] = $object->getClass();
|
||||
$data['trace'] = $object->getTrace();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null): bool
|
||||
{
|
||||
return $data instanceof FlattenException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
185
old.vendor/symfony/serializer/Normalizer/PropertyNormalizer.php
Normal file
185
old.vendor/symfony/serializer/Normalizer/PropertyNormalizer.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?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\Normalizer;
|
||||
|
||||
/**
|
||||
* Converts between objects and arrays by mapping properties.
|
||||
*
|
||||
* The normalization process looks for all the object's properties (public and private).
|
||||
* The result is a map from property names to property values. Property values
|
||||
* are normalized through the serializer.
|
||||
*
|
||||
* The denormalization first looks at the constructor of the given class to see
|
||||
* if any of the parameters have the same name as one of the properties. The
|
||||
* constructor is then called with all parameters or an exception is thrown if
|
||||
* any required parameters were not present as properties. Then the denormalizer
|
||||
* walks through the given map of property names to property values to see if a
|
||||
* property with the corresponding name exists. If found, the property gets the value.
|
||||
*
|
||||
* @author Matthieu Napoli <matthieu@mnapoli.fr>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyNormalizer extends AbstractObjectNormalizer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, $format = null)
|
||||
{
|
||||
return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return __CLASS__ === static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given class has any non-static property.
|
||||
*/
|
||||
private function supports(string $class): bool
|
||||
{
|
||||
$class = new \ReflectionClass($class);
|
||||
|
||||
// We look for at least one non-static property
|
||||
do {
|
||||
foreach ($class->getProperties() as $property) {
|
||||
if (!$property->isStatic()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while ($class = $class->getParentClass());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
if (!parent::isAllowedAttribute($classOrObject, $attribute, $format, $context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$reflectionProperty = $this->getReflectionProperty($classOrObject, $attribute);
|
||||
if ($reflectionProperty->isStatic()) {
|
||||
return false;
|
||||
}
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function extractAttributes($object, $format = null, array $context = [])
|
||||
{
|
||||
$reflectionObject = new \ReflectionObject($object);
|
||||
$attributes = [];
|
||||
$propertyValues = !method_exists($object, '__get') ? (array) $object : null;
|
||||
|
||||
do {
|
||||
foreach ($reflectionObject->getProperties() as $property) {
|
||||
if ((null !== $propertyValues && (
|
||||
($property->isPublic() && !\array_key_exists($property->name, $propertyValues))
|
||||
|| ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues))
|
||||
|| ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues))
|
||||
))
|
||||
|| !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[] = $property->name;
|
||||
}
|
||||
} while ($reflectionObject = $reflectionObject->getParentClass());
|
||||
|
||||
return array_unique($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = $this->getReflectionProperty($object, $attribute);
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Override visibility
|
||||
if (!$reflectionProperty->isPublic()) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
return $reflectionProperty->getValue($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = $this->getReflectionProperty($object, $attribute);
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($reflectionProperty->isStatic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Override visibility
|
||||
if (!$reflectionProperty->isPublic()) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
$reflectionProperty->setValue($object, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|object $classOrObject
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
private function getReflectionProperty($classOrObject, string $attribute): \ReflectionProperty
|
||||
{
|
||||
$reflectionClass = new \ReflectionClass($classOrObject);
|
||||
while (true) {
|
||||
try {
|
||||
return $reflectionClass->getProperty($attribute);
|
||||
} catch (\ReflectionException $e) {
|
||||
if (!$reflectionClass = $reflectionClass->getParentClass()) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user