Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,34 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu

$allowedAttributes = array();
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
$allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
$name = $attributeMetadata->getName();

if (
count(array_intersect($attributeMetadata->getGroups(), $context['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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?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\LogicException;

/**
* Base class for a normalizer dealing with objects.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
abstract class AbstractObjectNormalizer extends AbstractNormalizer
{
private $attributesCache = array();

/**
* {@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 ($this->isCircularReference($object, $context)) {
return $this->handleCircularReference($object);
}

$data = array();
$attributes = $this->getAttributes($object, $format, $context);

foreach ($attributes as $attribute) {
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);

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;
}

/**
* Gets and caches attributes for the given object, format and context.
*
* @param object $object
* @param string|null $format
* @param array $context
*
* @return string[]
*/
protected function getAttributes($object, $format = null, array $context)
{
$key = sprintf('%s-%s', get_class($object), serialize($context));

if (isset($this->attributesCache[$key])) {
return $this->attributesCache[$key];
}

$allowedAttributes = $this->getAllowedAttributes($object, $context, true);

if (false !== $allowedAttributes) {
return $this->attributesCache[$key] = $allowedAttributes;
}

return $this->attributesCache[$key] = $this->extractAttributes($object, $format, $context);
}

/**
* Extracts attributes to normalize from the class of the given object, format and context.
*
* @param object $object
* @param string|null $format
* @param array $context
*
* @return string[]
*/
abstract protected function extractAttributes($object, $format = null, array $context = array());

/**
* Gets the attribute value.
*
* @param object $object
* @param string $attribute
* @param string|null $format
* @param array $context
*
* @return mixed
*/
abstract protected function getAttributeValue($object, $attribute, $format = null, array $context = array());

/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
{
return class_exists($type);
}

/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
$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) {
$this->setAttributeValue($object, $attribute, $value, $format, $context);
}
}

return $object;
}

/**
* Sets attribute value.
*
* @param object $object
* @param string $attribute
* @param mixed $value
* @param string|null $format
* @param array $context
*/
abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array());
}
113 changes: 57 additions & 56 deletions src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

namespace Symfony\Component\Serializer\Normalizer;

use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;

/**
Expand All @@ -36,59 +34,8 @@
* @author Nils Adermann <naderman@naderman.de>
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class GetSetMethodNormalizer extends AbstractNormalizer
class GetSetMethodNormalizer extends AbstractObjectNormalizer
{
/**
* {@inheritdoc}
*
* @throws LogicException
* @throws CircularReferenceException
*/
public function normalize($object, $format = null, array $context = array())
{
if ($this->isCircularReference($object, $context)) {
return $this->handleCircularReference($object);
}

$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);

$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3));
if (in_array($attributeName, $this->ignoredAttributes)) {
continue;
}

if (false !== $allowedAttributes && !in_array($attributeName, $allowedAttributes)) {
continue;
}

$attributeValue = $method->invoke($object);
if (isset($this->callbacks[$attributeName])) {
$attributeValue = call_user_func($this->callbacks[$attributeName], $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', $attributeName));
}

$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
}

if ($this->nameConverter) {
$attributeName = $this->nameConverter->normalize($attributeName);
}

$attributes[$attributeName] = $attributeValue;
}
}

return $attributes;
}

/**
* {@inheritdoc}
*
Expand Down Expand Up @@ -128,15 +75,15 @@ public function denormalize($data, $class, $format = null, array $context = arra
*/
public function supportsNormalization($data, $format = null)
{
return is_object($data) && !$data instanceof \Traversable && $this->supports(get_class($data));
return parent::supportsNormalization($data, $format) && $this->supports(get_class($data));
}

/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
{
return class_exists($type) && $this->supports($type);
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
}

/**
Expand Down Expand Up @@ -179,4 +126,58 @@ private function isGetMethod(\ReflectionMethod $method)
)
;
}

/**
* {@inheritdoc}
*/
protected function extractAttributes($object, $format = null, array $context = array())
{
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);

$attributes = array();
foreach ($reflectionMethods as $method) {
if (!$this->isGetMethod($method)) {
continue;
}

$attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3));

if ($this->isAllowedAttribute($object, $attributeName)) {
$attributes[] = $attributeName;
}
}

return $attributes;
}

/**
* {@inheritdoc}
*/
protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
{
$ucfirsted = ucfirst($attribute);

$getter = 'get'.$ucfirsted;
if (is_callable(array($object, $getter))) {
return $object->$getter();
}

$isser = 'is'.$ucfirsted;
if (is_callable(array($object, $isser))) {
return $object->$isser();
}
}

/**
* {@inheritdoc}
*/
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
{
$setter = 'set'.ucfirst($attribute);

if (is_callable(array($object, $setter))) {
$object->$setter($value);
}
}
}
Loading