Mercurial > hg > isophonics-drupal-site
diff vendor/symfony/serializer/Normalizer/AbstractNormalizer.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/symfony/serializer/Normalizer/AbstractNormalizer.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,375 @@ +<?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\RuntimeException; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\SerializerAwareInterface; + +/** + * Normalizer implementation. + * + * @author Kévin Dunglas <dunglas@gmail.com> + */ +abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface +{ + const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit'; + const OBJECT_TO_POPULATE = 'object_to_populate'; + const GROUPS = 'groups'; + + /** + * @var int + */ + protected $circularReferenceLimit = 1; + + /** + * @var callable + */ + protected $circularReferenceHandler; + + /** + * @var ClassMetadataFactoryInterface|null + */ + protected $classMetadataFactory; + + /** + * @var NameConverterInterface|null + */ + protected $nameConverter; + + /** + * @var array + */ + protected $callbacks = array(); + + /** + * @var array + */ + protected $ignoredAttributes = array(); + + /** + * @var array + */ + protected $camelizedAttributes = array(); + + /** + * Sets the {@link ClassMetadataFactoryInterface} to use. + * + * @param ClassMetadataFactoryInterface|null $classMetadataFactory + * @param NameConverterInterface|null $nameConverter + */ + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null) + { + $this->classMetadataFactory = $classMetadataFactory; + $this->nameConverter = $nameConverter; + } + + /** + * Set circular reference limit. + * + * @param int $circularReferenceLimit limit of iterations for the same object + * + * @return self + */ + public function setCircularReferenceLimit($circularReferenceLimit) + { + $this->circularReferenceLimit = $circularReferenceLimit; + + return $this; + } + + /** + * Set circular reference handler. + * + * @param callable $circularReferenceHandler + * + * @return self + */ + public function setCircularReferenceHandler(callable $circularReferenceHandler) + { + $this->circularReferenceHandler = $circularReferenceHandler; + + return $this; + } + + /** + * Set normalization callbacks. + * + * @param callable[] $callbacks help normalize the result + * + * @return self + * + * @throws InvalidArgumentException if a non-callable callback is set + */ + public function setCallbacks(array $callbacks) + { + foreach ($callbacks as $attribute => $callback) { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'The given callback for attribute "%s" is not callable.', + $attribute + )); + } + } + $this->callbacks = $callbacks; + + return $this; + } + + /** + * Set ignored attributes for normalization and denormalization. + * + * @param array $ignoredAttributes + * + * @return self + */ + public function setIgnoredAttributes(array $ignoredAttributes) + { + $this->ignoredAttributes = $ignoredAttributes; + + return $this; + } + + /** + * 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); + + if (isset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash])) { + if ($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] >= $this->circularReferenceLimit) { + unset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash]); + + return true; + } + + ++$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash]; + } else { + $context[static::CIRCULAR_REFERENCE_LIMIT][$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. + * + * @param object $object + * + * @return mixed + * + * @throws CircularReferenceException + */ + protected function handleCircularReference($object) + { + if ($this->circularReferenceHandler) { + return call_user_func($this->circularReferenceHandler, $object); + } + + throw new CircularReferenceException(sprintf('A circular reference has been detected (configured limit: %d).', $this->circularReferenceLimit)); + } + + /** + * Gets attributes to normalize using groups. + * + * @param string|object $classOrObject + * @param array $context + * @param bool $attributesAsString If false, return an array of {@link AttributeMetadataInterface} + * + * @return string[]|AttributeMetadataInterface[]|bool + */ + protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false) + { + if (!$this->classMetadataFactory || !isset($context[static::GROUPS]) || !is_array($context[static::GROUPS])) { + return false; + } + + $allowedAttributes = array(); + foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) { + $name = $attributeMetadata->getName(); + + if ( + count(array_intersect($attributeMetadata->getGroups(), $context[static::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 + * @param array $context + * + * @return bool + */ + protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array()) + { + return !in_array($attribute, $this->ignoredAttributes); + } + + /** + * 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 array $data + * @param string $class + * @param array $context + * @param \ReflectionClass $reflectionClass + * @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 array $data + * @param string $class + * @param array $context + * @param \ReflectionClass $reflectionClass + * @param array|bool $allowedAttributes + * @param string|null $format + * + * @return object + * + * @throws RuntimeException + */ + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes/*, $format = null*/) + { + if (func_num_args() >= 6) { + $format = func_get_arg(5); + } else { + if (__CLASS__ !== get_class($this)) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s::%s() will have a 6th `$format = null` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); + } + } + + $format = null; + } + + if ( + isset($context[static::OBJECT_TO_POPULATE]) && + is_object($context[static::OBJECT_TO_POPULATE]) && + $context[static::OBJECT_TO_POPULATE] instanceof $class + ) { + $object = $context[static::OBJECT_TO_POPULATE]; + unset($context[static::OBJECT_TO_POPULATE]); + + return $object; + } + + $constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes); + if ($constructor) { + $constructorParameters = $constructor->getParameters(); + + $params = array(); + foreach ($constructorParameters as $constructorParameter) { + $paramName = $constructorParameter->name; + $key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName; + + $allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes); + $ignored = in_array($paramName, $this->ignoredAttributes); + if (method_exists($constructorParameter, 'isVariadic') && $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)); + } + + $params = array_merge($params, $data[$paramName]); + } + } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { + $parameterData = $data[$key]; + try { + if (null !== $constructorParameter->getClass()) { + if (!$this->serializer instanceof DenormalizerInterface) { + throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), static::class)); + } + $parameterClass = $constructorParameter->getClass()->getName(); + $parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $context); + } + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e); + } + + // Don't run set for a parameter passed to the constructor + $params[] = $parameterData; + unset($data[$key]); + } elseif ($constructorParameter->isDefaultValueAvailable()) { + $params[] = $constructorParameter->getDefaultValue(); + } else { + throw new RuntimeException( + 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(); + } +}