use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
-use Symfony\Component\Serializer\Exception\CircularReferenceException;
-use Symfony\Component\Serializer\Exception\LogicException;
+use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
-class ObjectNormalizer extends AbstractNormalizer
+class ObjectNormalizer extends AbstractObjectNormalizer
{
- private $attributesCache = array();
-
/**
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
- public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null)
+ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null)
{
- parent::__construct($classMetadataFactory, $nameConverter);
+ parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor);
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
}
/**
* {@inheritdoc}
*/
- public function supportsNormalization($data, $format = null)
- {
- return is_object($data) && !$data instanceof \Traversable;
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws CircularReferenceException
- */
- public function normalize($object, $format = null, array $context = array())
- {
- if (!isset($context['cache_key'])) {
- $context['cache_key'] = $this->getCacheKey($context);
- }
- if ($this->isCircularReference($object, $context)) {
- return $this->handleCircularReference($object);
- }
-
- $data = array();
- $attributes = $this->getAttributes($object, $context);
-
- foreach ($attributes as $attribute) {
- if (in_array($attribute, $this->ignoredAttributes)) {
- continue;
- }
-
- $attributeValue = $this->propertyAccessor->getValue($object, $attribute);
-
- if (isset($this->callbacks[$attribute])) {
- $attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue);
- }
-
- if (null !== $attributeValue && !is_scalar($attributeValue)) {
- if (!$this->serializer instanceof NormalizerInterface) {
- throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attribute));
- }
-
- $attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
- }
-
- if ($this->nameConverter) {
- $attribute = $this->nameConverter->normalize($attribute);
- }
-
- $data[$attribute] = $attributeValue;
- }
-
- return $data;
- }
-
- /**
- * {@inheritdoc}
- */
- public function supportsDenormalization($data, $type, $format = null)
- {
- return class_exists($type);
- }
-
- /**
- * {@inheritdoc}
- */
- public function denormalize($data, $class, $format = null, array $context = array())
- {
- if (!isset($context['cache_key'])) {
- $context['cache_key'] = $this->getCacheKey($context);
- }
- $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
- $normalizedData = $this->prepareForDenormalization($data);
-
- $reflectionClass = new \ReflectionClass($class);
- $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
-
- foreach ($normalizedData as $attribute => $value) {
- if ($this->nameConverter) {
- $attribute = $this->nameConverter->denormalize($attribute);
- }
-
- $allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes);
- $ignored = in_array($attribute, $this->ignoredAttributes);
-
- if ($allowed && !$ignored) {
- try {
- $this->propertyAccessor->setValue($object, $attribute, $value);
- } catch (NoSuchPropertyException $exception) {
- // Properties not found are ignored
- }
- }
- }
-
- return $object;
- }
-
- private function getCacheKey(array $context)
- {
- try {
- return md5(serialize($context));
- } catch (\Exception $exception) {
- // The context cannot be serialized, skip the cache
- return false;
- }
- }
-
- /**
- * Gets and caches attributes for this class and context.
- *
- * @param object $object
- * @param array $context
- *
- * @return string[]
- */
- private function getAttributes($object, array $context)
- {
- $class = 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;
- }
-
- if (isset($this->attributesCache[$class])) {
- return $this->attributesCache[$class];
- }
-
- return $this->attributesCache[$class] = $this->extractAttributes($object);
- }
-
- /**
- * Extracts attributes for this class and context.
- *
- * @param object $object
- *
- * @return string[]
- */
- private function extractAttributes($object)
+ protected function extractAttributes($object, $format = null, array $context = array())
{
// If not using groups, detect manually
$attributes = array();
}
$name = $reflMethod->name;
+ $attributeName = null;
if (0 === strpos($name, 'get') || 0 === strpos($name, 'has')) {
// getters and hassers
- $propertyName = substr($name, 3);
+ $attributeName = substr($name, 3);
- if (!$reflClass->hasProperty($propertyName)) {
- $propertyName = lcfirst($propertyName);
+ if (!$reflClass->hasProperty($attributeName)) {
+ $attributeName = lcfirst($attributeName);
}
-
- $attributes[$propertyName] = true;
} elseif (strpos($name, 'is') === 0) {
// issers
- $propertyName = substr($name, 2);
+ $attributeName = substr($name, 2);
- if (!$reflClass->hasProperty($propertyName)) {
- $propertyName = lcfirst($propertyName);
+ if (!$reflClass->hasProperty($attributeName)) {
+ $attributeName = lcfirst($attributeName);
}
+ }
- $attributes[$propertyName] = true;
+ if (null !== $attributeName && $this->isAllowedAttribute($object, $attributeName, $format, $context)) {
+ $attributes[$attributeName] = true;
}
}
// properties
foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) {
- if ($reflProperty->isStatic()) {
+ if ($reflProperty->isStatic() || !$this->isAllowedAttribute($object, $reflProperty->name, $format, $context)) {
continue;
}
return array_keys($attributes);
}
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
+ {
+ return $this->propertyAccessor->getValue($object, $attribute);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
+ {
+ try {
+ $this->propertyAccessor->setValue($object, $attribute, $value);
+ } catch (NoSuchPropertyException $exception) {
+ // Properties not found are ignored
+ }
+ }
}