[Serializer] Refactoring of metadata

This commit is contained in:
Kévin Dunglas 2015-02-25 23:32:01 +01:00
parent 37e0fa18c6
commit 8534505db5
26 changed files with 666 additions and 164 deletions

View File

@ -0,0 +1,94 @@
<?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\Mapping;
/**
* {@inheritdoc}
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class AttributeMetadata implements AttributeMetadataInterface
{
/**
* @var string
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getName()} instead.
*/
public $name;
/**
* @var array
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getGroups()} instead.
*/
public $groups = array();
/**
* Constructs a metadata for the given attribute.
*
* @param string $name
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function addGroup($group)
{
if (!in_array($group, $this->groups)) {
$this->groups[] = $group;
}
}
/**
* {@inheritdoc}
*/
public function getGroups()
{
return $this->groups;
}
/**
* {@inheritdoc}
*/
public function merge(AttributeMetadataInterface $attributeMetadata)
{
foreach ($attributeMetadata->getGroups() as $group) {
$this->addGroup($group);
}
}
/**
* Returns the names of the properties that should be serialized.
*
* @return string[]
*/
public function __sleep()
{
return array('name', 'groups');
}
}

View File

@ -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\Mapping;
/**
* Stores metadata needed for serializing and deserializing attributes.
*
* Primarily, the metadata stores serialization groups.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface AttributeMetadataInterface
{
/**
* Gets the attribute name.
*
* @return string
*/
public function getName();
/**
* Adds this attribute to the given group.
*
* @param string $group
*/
public function addGroup($group);
/**
* Gets groups of this attribute.
*
* @return string[]
*/
public function getGroups();
/**
* Merges an {@see AttributeMetadataInterface} with in the current one.
*
* @param AttributeMetadataInterface $attributeMetadata
*/
public function merge(AttributeMetadataInterface $attributeMetadata);
}

View File

@ -12,31 +12,29 @@
namespace Symfony\Component\Serializer\Mapping;
/**
* Stores all metadata needed for serializing objects of specific class.
*
* Primarily, the metadata stores serialization groups.
* {@inheritdoc}
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ClassMetadata
class ClassMetadata implements ClassMetadataInterface
{
/**
* @var string
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getClassName()} instead.
* {@link getName()} instead.
*/
public $name;
/**
* @var array
* @var AttributeMetadataInterface[]
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getGroups()} instead.
* {@link getAttributesMetadata()} instead.
*/
public $attributesGroups = array();
public $attributesMetadata = array();
/**
* @var \ReflectionClass
@ -54,66 +52,50 @@ class ClassMetadata
}
/**
* Returns the name of the backing PHP class.
*
* @return string The name of the backing class.
* {@inheritdoc}
*/
public function getClassName()
public function getName()
{
return $this->name;
}
/**
* Gets serialization groups.
*
* @return array
* {@inheritdoc}
*/
public function getAttributesGroups()
public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata)
{
return $this->attributesGroups;
$this->attributesMetadata[$attributeMetadata->getName()] = $attributeMetadata;
}
/**
* Adds an attribute to a serialization group
*
* @param string $attribute
* @param string $group
* @throws \InvalidArgumentException
* {@inheritdoc}
*/
public function addAttributeGroup($attribute, $group)
public function getAttributesMetadata()
{
if (!is_string($attribute) || !is_string($group)) {
throw new \InvalidArgumentException('Arguments must be strings.');
}
if (!isset($this->groups[$group]) || !in_array($attribute, $this->attributesGroups[$group])) {
$this->attributesGroups[$group][] = $attribute;
}
return $this->attributesMetadata;
}
/**
* Merges attributes' groups.
*
* @param ClassMetadata $classMetadata
* {@inheritdoc}
*/
public function mergeAttributesGroups(ClassMetadata $classMetadata)
public function merge(ClassMetadataInterface $classMetadata)
{
foreach ($classMetadata->getAttributesGroups() as $group => $attributes) {
foreach ($attributes as $attribute) {
$this->addAttributeGroup($attribute, $group);
foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) {
if (isset($this->attributesMetadata[$attributeMetadata->getName()])) {
$this->attributesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata);
} else {
$this->addAttributeMetadata($attributeMetadata);
}
}
}
/**
* Returns a ReflectionClass instance for this class.
*
* @return \ReflectionClass
* {@inheritdoc}
*/
public function getReflectionClass()
{
if (!$this->reflClass) {
$this->reflClass = new \ReflectionClass($this->getClassName());
$this->reflClass = new \ReflectionClass($this->getName());
}
return $this->reflClass;
@ -128,7 +110,7 @@ class ClassMetadata
{
return array(
'name',
'attributesGroups',
'attributes',
);
}
}

View File

@ -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\Mapping;
/**
* Stores metadata needed for serializing and deserializing objects of specific class.
*
* Primarily, the metadata stores the list of attributes to serialize or deserialize.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ClassMetadataInterface
{
/**
* Returns the name of the backing PHP class.
*
* @return string The name of the backing class.
*/
public function getName();
/**
* Adds an {@link AttributeMetadataInterface}.
*
* @param AttributeMetadataInterface $attributeMetadata
*/
public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata);
/**
* Gets the list of {@link AttributeMetadataInterface}.
*
* @return AttributeMetadataInterface[]
*/
public function getAttributesMetadata();
/**
* Merges a {@link ClassMetadataInterface} in the current one.
*
* @param ClassMetadataInterface $classMetadata
*/
public function merge(ClassMetadataInterface $classMetadata);
/**
* Returns a {@link \ReflectionClass} instance for this class.
*
* @return \ReflectionClass
*/
public function getReflectionClass();
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Mapping\Factory;
use Doctrine\Common\Cache\Cache;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
@ -20,7 +21,7 @@ use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ClassMetadataFactory
class ClassMetadataFactory implements ClassMetadataFactoryInterface
{
/**
* @var LoaderInterface
@ -46,29 +47,13 @@ class ClassMetadataFactory
}
/**
* If the method was called with the same class name (or an object of that
* class) before, the same metadata instance is returned.
*
* If the factory was configured with a cache, this method will first look
* for an existing metadata instance in the cache. If an existing instance
* is found, it will be returned without further ado.
*
* Otherwise, a new metadata instance is created. If the factory was
* configured with a loader, the metadata is passed to the
* {@link LoaderInterface::loadClassMetadata()} method for further
* configuration. At last, the new object is returned.
*
* @param string|object $value
*
* @return ClassMetadata
*
* @throws \InvalidArgumentException
* {@inheritdoc}
*/
public function getMetadataFor($value)
{
$class = $this->getClass($value);
if (!$class) {
throw new \InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: %s', gettype($value)));
throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value)));
}
if (isset($this->loadedClasses[$class])) {
@ -80,40 +65,33 @@ class ClassMetadataFactory
}
if (!class_exists($class) && !interface_exists($class)) {
throw new \InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class));
throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class));
}
$metadata = new ClassMetadata($class);
$classMetadata = new ClassMetadata($class);
$this->loader->loadClassMetadata($classMetadata);
$reflClass = $metadata->getReflectionClass();
$reflectionClass = $classMetadata->getReflectionClass();
// Include groups from the parent class
if ($parent = $reflClass->getParentClass()) {
$metadata->mergeAttributesGroups($this->getMetadataFor($parent->name));
// Include metadata from the parent class
if ($parent = $reflectionClass->getParentClass()) {
$classMetadata->merge($this->getMetadataFor($parent->name));
}
// Include groups from all implemented interfaces
foreach ($reflClass->getInterfaces() as $interface) {
$metadata->mergeAttributesGroups($this->getMetadataFor($interface->name));
}
if ($this->loader) {
$this->loader->loadClassMetadata($metadata);
// Include metadata from all implemented interfaces
foreach ($reflectionClass->getInterfaces() as $interface) {
$classMetadata->merge($this->getMetadataFor($interface->name));
}
if ($this->cache) {
$this->cache->save($class, $metadata);
$this->cache->save($class, $classMetadata);
}
return $this->loadedClasses[$class] = $metadata;
return $this->loadedClasses[$class] = $classMetadata;
}
/**
* Checks if class has metadata.
*
* @param mixed $value
*
* @return bool
* {@inheritdoc}
*/
public function hasMetadataFor($value)
{

View File

@ -0,0 +1,53 @@
<?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\Mapping\Factory;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Returns a {@see ClassMetadataInterface}.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ClassMetadataFactoryInterface
{
/**
* If the method was called with the same class name (or an object of that
* class) before, the same metadata instance is returned.
*
* If the factory was configured with a cache, this method will first look
* for an existing metadata instance in the cache. If an existing instance
* is found, it will be returned without further ado.
*
* Otherwise, a new metadata instance is created. If the factory was
* configured with a loader, the metadata is passed to the
* {@link LoaderInterface::loadClassMetadata()} method for further
* configuration. At last, the new object is returned.
*
* @param string|object $value
*
* @return ClassMetadataInterface
*
* @throws InvalidArgumentException
*/
public function getMetadataFor($value);
/**
* Checks if class has metadata.
*
* @param mixed $value
*
* @return bool
*/
public function hasMetadataFor($value);
}

View File

@ -14,7 +14,8 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Annotation loader.
@ -39,18 +40,25 @@ class AnnotationLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function loadClassMetadata(ClassMetadata $metadata)
public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
$reflClass = $metadata->getReflectionClass();
$className = $reflClass->name;
$reflectionClass = $classMetadata->getReflectionClass();
$className = $reflectionClass->name;
$loaded = false;
foreach ($reflClass->getProperties() as $property) {
if ($property->getDeclaringClass()->name === $className) {
$attributesMetadata = $classMetadata->getAttributesMetadata();
foreach ($reflectionClass->getProperties() as $property) {
if (!isset($attributeMetadata[$property->name])) {
$attributesMetadata[$property->name] = new AttributeMetadata($property->name);
$classMetadata->addAttributeMetadata($attributesMetadata[$property->name]);
}
if ($property->getDeclaringClass()->name == $className) {
foreach ($this->reader->getPropertyAnnotations($property) as $groups) {
if ($groups instanceof Groups) {
foreach ($groups->getGroups() as $group) {
$metadata->addAttributeGroup($property->name, $group);
$attributesMetadata[$property->name]->addGroup($group);
}
}
@ -59,13 +67,22 @@ class AnnotationLoader implements LoaderInterface
}
}
foreach ($reflClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name === $className) {
foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name == $className) {
foreach ($this->reader->getMethodAnnotations($method) as $groups) {
if ($groups instanceof Groups) {
if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) {
$attributeName = lcfirst($matches[2]);
if (isset($attributesMetadata[$attributeName])) {
$attributeMetadata = $attributesMetadata[$attributeName];
} else {
$attributesMetadata[$attributeName] = $attributeMetadata = new AttributeMetadata($attributeName);
$classMetadata->addAttributeMetadata($attributeMetadata);
}
foreach ($groups->getGroups() as $group) {
$metadata->addAttributeGroup(lcfirst($matches[2]), $group);
$attributeMetadata->addGroup($group);
}
} else {
throw new MappingException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name));

View File

@ -13,8 +13,16 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
/**
* Base class for all file based loaders.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
abstract class FileLoader implements LoaderInterface
{
/**
* @var string
*/
protected $file;
/**
@ -22,8 +30,7 @@ abstract class FileLoader implements LoaderInterface
*
* @param string $file The mapping file to load
*
* @throws MappingException if the mapping file does not exist
* @throws MappingException if the mapping file is not readable
* @throws MappingException if the mapping file does not exist or is not readable
*/
public function __construct($file)
{

View File

@ -12,24 +12,28 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Calls multiple LoaderInterface instances in a chain
* Calls multiple {@link LoaderInterface} instances in a chain.
*
* This class accepts multiple instances of LoaderInterface to be passed to the
* constructor. When loadClassMetadata() is called, the same method is called
* constructor. When {@link loadClassMetadata()} is called, the same method is called
* in <em>all</em> of these loaders, regardless of whether any of them was
* successful or not.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class LoaderChain implements LoaderInterface
{
protected $loaders;
/**
* @var LoaderInterface[]
*/
private $loaders;
/**
* Accepts a list of LoaderInterface instances
* Accepts a list of LoaderInterface instances.
*
* @param LoaderInterface[] $loaders An array of LoaderInterface instances
*
@ -49,7 +53,7 @@ class LoaderChain implements LoaderInterface
/**
* {@inheritdoc}
*/
public function loadClassMetadata(ClassMetadata $metadata)
public function loadClassMetadata(ClassMetadataInterface $metadata)
{
$success = false;

View File

@ -11,10 +11,10 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Loads class metadata.
* Loads {@link ClassMetadataInterface}.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
@ -23,9 +23,9 @@ interface LoaderInterface
/**
* Load class metadata.
*
* @param ClassMetadata $metadata A metadata
* @param ClassMetadataInterface $classMetadata A metadata
*
* @return bool
*/
public function loadClassMetadata(ClassMetadata $metadata);
public function loadClassMetadata(ClassMetadataInterface $classMetadata);
}

View File

@ -13,7 +13,8 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Loads XML mapping files.
@ -23,7 +24,7 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata;
class XmlFileLoader extends FileLoader
{
/**
* An array of SimpleXMLElement instances.
* An array of {@class \SimpleXMLElement} instances.
*
* @var \SimpleXMLElement[]|null
*/
@ -32,7 +33,7 @@ class XmlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
public function loadClassMetadata(ClassMetadata $metadata)
public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
if (null === $this->classes) {
$this->classes = array();
@ -43,12 +44,23 @@ class XmlFileLoader extends FileLoader
}
}
if (isset($this->classes[$metadata->getClassName()])) {
$xml = $this->classes[$metadata->getClassName()];
$attributesMetadata = $classMetadata->getAttributesMetadata();
if (isset($this->classes[$classMetadata->getName()])) {
$xml = $this->classes[$classMetadata->getName()];
foreach ($xml->attribute as $attribute) {
$attributeName = (string) $attribute['name'];
if (isset($attributesMetadata[$attributeName])) {
$attributeMetadata = $attributesMetadata[$attributeName];
} else {
$attributeMetadata = new AttributeMetadata($attributeName);
$classMetadata->addAttributeMetadata($attributeMetadata);
}
foreach ($attribute->group as $group) {
$metadata->addAttributeGroup((string) $attribute['name'], (string) $group);
$attributeMetadata->addGroup((string) $group);
}
}
@ -59,7 +71,7 @@ class XmlFileLoader extends FileLoader
}
/**
* Parse a XML File.
* Parses a XML File.
*
* @param string $file Path of file
*

View File

@ -12,11 +12,12 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
use Symfony\Component\Yaml\Parser;
/**
* YAML File Loader
* YAML File Loader.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
@ -34,7 +35,7 @@ class YamlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
public function loadClassMetadata(ClassMetadata $metadata)
public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
if (null === $this->classes) {
if (!stream_is_local($this->file)) {
@ -59,14 +60,22 @@ class YamlFileLoader extends FileLoader
$this->classes = $classes;
}
if (isset($this->classes[$metadata->getClassName()])) {
$yaml = $this->classes[$metadata->getClassName()];
if (isset($this->classes[$classMetadata->getName()])) {
$yaml = $this->classes[$classMetadata->getName()];
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
$attributesMetadata = $classMetadata->getAttributesMetadata();
foreach ($yaml['attributes'] as $attribute => $data) {
if (isset($attributesMetadata[$attribute])) {
$attributeMetadata = $attributesMetadata[$attribute];
} else {
$attributeMetadata = new AttributeMetadata($attribute);
$classMetadata->addAttributeMetadata($attributeMetadata);
}
if (isset($data['groups'])) {
foreach ($data['groups'] as $group) {
$metadata->addAttributeGroup($attribute, $group);
$attributeMetadata->addGroup($group);
}
}
}

View File

@ -15,7 +15,8 @@ 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\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
@ -26,21 +27,42 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*/
abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
/**
* @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 ClassMetadataFactory} to use.
* Sets the {@link ClassMetadataFactoryInterface} to use.
*
* @param ClassMetadataFactory|null $classMetadataFactory
* @param ClassMetadataFactoryInterface|null $classMetadataFactory
* @param NameConverterInterface|null $nameConverter
*/
public function __construct(ClassMetadataFactory $classMetadataFactory = null, NameConverterInterface $nameConverter = null)
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->nameConverter = $nameConverter;
@ -219,19 +241,21 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
* Gets attributes to normalize using groups.
*
* @param string|object $classOrObject
* @param array $context
* @return array|bool
* @param array $context
* @param bool $attributesAsString If false, return an array of {@link AttributeMetadataInterface}
*
* @return string[]|AttributeMetadataInterface[]|bool
*/
protected function getAllowedAttributes($classOrObject, array $context)
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
{
if (!$this->classMetadataFactory || !isset($context['groups']) || !is_array($context['groups'])) {
return false;
}
$allowedAttributes = array();
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesGroups() as $group => $attributes) {
if (in_array($group, $context['groups'])) {
$allowedAttributes = array_merge($allowedAttributes, $attributes);
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
$allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
}
}

View File

@ -52,7 +52,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$allowedAttributes = $this->getAllowedAttributes($object, $context);
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
$attributes = array();
foreach ($reflectionMethods as $method) {
@ -97,7 +97,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
$allowedAttributes = $this->getAllowedAttributes($class, $context);
$allowedAttributes = $this->getAllowedAttributes($class, $context, true);
$normalizedData = $this->prepareForDenormalization($data);
$reflectionClass = new \ReflectionClass($class);

View File

@ -46,7 +46,7 @@ class PropertyNormalizer extends AbstractNormalizer
$reflectionObject = new \ReflectionObject($object);
$attributes = array();
$allowedAttributes = $this->getAllowedAttributes($object, $context);
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
foreach ($reflectionObject->getProperties() as $property) {
if (in_array($property->name, $this->ignoredAttributes)) {
@ -89,7 +89,7 @@ class PropertyNormalizer extends AbstractNormalizer
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
$allowedAttributes = $this->getAllowedAttributes($class, $context);
$allowedAttributes = $this->getAllowedAttributes($class, $context, true);
$data = $this->prepareForDenormalization($data);
$reflectionClass = new \ReflectionClass($class);

View File

@ -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\Tests\Mapping;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class AttributeMetadataTest extends \PHPUnit_Framework_TestCase
{
public function testInterface()
{
$attributeMetadata = new AttributeMetadata('name');
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface', $attributeMetadata);
}
public function testGetName()
{
$attributeMetadata = new AttributeMetadata('name');
$this->assertEquals('name', $attributeMetadata->getName());
}
public function testGroups()
{
$attributeMetadata = new AttributeMetadata('group');
$attributeMetadata->addGroup('a');
$attributeMetadata->addGroup('a');
$attributeMetadata->addGroup('b');
$this->assertEquals(array('a', 'b'), $attributeMetadata->getGroups());
}
public function testMerge()
{
$attributeMetadata1 = new AttributeMetadata('a1');
$attributeMetadata1->addGroup('a');
$attributeMetadata1->addGroup('b');
$attributeMetadata2 = new AttributeMetadata('a2');
$attributeMetadata2->addGroup('a');
$attributeMetadata2->addGroup('c');
$attributeMetadata1->merge($attributeMetadata2);
$this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups());
}
}

View File

@ -0,0 +1,65 @@
<?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\Tests\Mapping;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ClassMetadataTest extends \PHPUnit_Framework_TestCase
{
public function testInterface()
{
$classMetadata = new ClassMetadata('name');
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\ClassMetadataInterface', $classMetadata);
}
public function testAttributeMetadata()
{
$classMetadata = new ClassMetadata('c');
$a1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
$a1->method('getName')->willReturn('a1');
$a2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
$a2->method('getName')->willReturn('a2');
$classMetadata->addAttributeMetadata($a1);
$classMetadata->addAttributeMetadata($a2);
$this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getAttributesMetadata());
}
public function testMerge()
{
$classMetadata1 = new ClassMetadata('c1');
$classMetadata2 = new ClassMetadata('c2');
$ac1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
$ac1->method('getName')->willReturn('a1');
$ac1->method('getGroups')->willReturn(array('a', 'b'));
$ac2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
$ac2->method('getName')->willReturn('a1');
$ac2->method('getGroups')->willReturn(array('b', 'c'));
$classMetadata1->addAttributeMetadata($ac1);
$classMetadata2->addAttributeMetadata($ac2);
$classMetadata1->merge($classMetadata2);
$ac1->method('getGroups')->willReturn('a', 'b', 'c');
$this->assertEquals(array('a1' => $ac1), $classMetadata2->getAttributesMetadata());
}
}

View File

@ -14,21 +14,26 @@ namespace Symfony\Component\Serializer\Tests\Mapping\Factory;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
require_once __DIR__.'/../../../Annotation/Groups.php';
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase
{
public function testInterface()
{
$classMetadata = new ClassMetadataFactory(new LoaderChain(array()));
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory', $classMetadata);
}
public function testGetMetadataFor()
{
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$metadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$classMetadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $metadata);
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $classMetadata);
}
public function testHasMetadataFor()

View File

@ -16,42 +16,51 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
require_once __DIR__.'/../../../Annotation/Groups.php';
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class AnnotationLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var AnnotationLoader
*/
private $loader;
protected function setUp()
{
$this->loader = new AnnotationLoader(new AnnotationReader());
}
public function testInterface()
{
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
}
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
$loader = new AnnotationLoader(new AnnotationReader());
$metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$this->assertTrue($loader->loadClassMetadata($metadata));
$this->assertTrue($this->loader->loadClassMetadata($classMetadata));
}
public function testLoadClassMetadata()
{
$loader = new AnnotationLoader(new AnnotationReader());
$metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$this->loader->loadClassMetadata($classMetadata);
$loader->loadClassMetadata($metadata);
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $metadata);
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata);
}
public function testLoadClassMetadataAndMerge()
{
$loader = new AnnotationLoader(new AnnotationReader());
$metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$parentMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent');
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$parentClassMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent');
$loader->loadClassMetadata($parentMetadata);
$metadata->mergeAttributesGroups($parentMetadata);
$this->loader->loadClassMetadata($parentClassMetadata);
$classMetadata->merge($parentClassMetadata);
$loader->loadClassMetadata($metadata);
$this->loader->loadClassMetadata($classMetadata);
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $metadata);
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $classMetadata);
}
}

View File

@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
*/
class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var XmlFileLoader
*/
private $loader;
/**
* @var ClassMetadata
*/
private $metadata;
public function setUp()
@ -29,6 +35,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
}
public function testInterface()
{
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
}
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
$this->assertTrue($this->loader->loadClassMetadata($this->metadata));

View File

@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
*/
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var YamlFileLoader
*/
private $loader;
/**
* @var ClassMetadata
*/
private $metadata;
public function setUp()
@ -29,6 +35,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
}
public function testInterface()
{
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
}
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
$this->assertTrue($this->loader->loadClassMetadata($this->metadata));

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Serializer\Tests\Mapping;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
/**
@ -22,22 +23,38 @@ class TestClassMetadataFactory
{
$expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$foo = new AttributeMetadata('foo');
$foo->addGroup('a');
$expected->addAttributeMetadata($foo);
$bar = new AttributeMetadata('bar');
$bar->addGroup('b');
$bar->addGroup('c');
$expected->addAttributeMetadata($bar);
$fooBar = new AttributeMetadata('fooBar');
$fooBar->addGroup('a');
$fooBar->addGroup('b');
$expected->addAttributeMetadata($fooBar);
$symfony = new AttributeMetadata('symfony');
$expected->addAttributeMetadata($symfony);
if ($withParent) {
$expected->addAttributeGroup('kevin', 'a');
$expected->addAttributeGroup('coopTilleuls', 'a');
$expected->addAttributeGroup('coopTilleuls', 'b');
$kevin = new AttributeMetadata('kevin');
$kevin->addGroup('a');
$expected->addAttributeMetadata($kevin);
$coopTilleuls = new AttributeMetadata('coopTilleuls');
$coopTilleuls->addGroup('a');
$coopTilleuls->addGroup('b');
$expected->addAttributeMetadata($coopTilleuls);
}
if ($withInterface) {
$expected->addAttributeGroup('symfony', 'a');
$symfony->addGroup('a');
}
$expected->addAttributeGroup('foo', 'a');
$expected->addAttributeGroup('bar', 'b');
$expected->addAttributeGroup('bar', 'c');
$expected->addAttributeGroup('fooBar', 'a');
$expected->addAttributeGroup('fooBar', 'b');
// load reflection class so that the comparison passes
$expected->getReflectionClass();
@ -47,9 +64,15 @@ class TestClassMetadataFactory
public static function createXmlCLassMetadata()
{
$expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
$expected->addAttributeGroup('foo', 'group1');
$expected->addAttributeGroup('foo', 'group2');
$expected->addAttributeGroup('bar', 'group2');
$foo = new AttributeMetadata('foo');
$foo->addGroup('group1');
$foo->addGroup('group2');
$expected->addAttributeMetadata($foo);
$bar = new AttributeMetadata('bar');
$bar->addGroup('group2');
$expected->addAttributeMetadata($bar);
return $expected;
}

View File

@ -18,6 +18,12 @@ use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter
*/
class CamelCaseToSnakeCaseNameConverterTest extends \PHPUnit_Framework_TestCase
{
public function testInterface()
{
$attributeMetadata = new CamelCaseToSnakeCaseNameConverter();
$this->assertInstanceOf('Symfony\Component\Serializer\NameConverter\NameConverterInterface', $attributeMetadata);
}
/**
* @dataProvider attributeProvider
*/

View File

@ -17,12 +17,23 @@ use Symfony\Component\Serializer\Serializer;
class CustomNormalizerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var CustomNormalizer
*/
private $normalizer;
protected function setUp()
{
$this->normalizer = new CustomNormalizer();
$this->normalizer->setSerializer(new Serializer());
}
public function testInterface()
{
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer);
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer);
}
public function testSerialize()
{
$obj = new ScalarDummy();

View File

@ -40,6 +40,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->normalizer->setSerializer($this->serializer);
}
public function testInterface()
{
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer);
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer);
}
public function testNormalize()
{
$obj = new GetSetDummy();

View File

@ -22,6 +22,17 @@ use Symfony\Component\Serializer\Tests\Normalizer\TestDenormalizer;
class SerializerTest extends \PHPUnit_Framework_TestCase
{
public function testInterface()
{
$serializer = new Serializer();
$this->assertInstanceOf('Symfony\Component\Serializer\SerializerInterface', $serializer);
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $serializer);
$this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $serializer);
$this->assertInstanceOf('Symfony\Component\Serializer\Encoder\EncoderInterface', $serializer);
$this->assertInstanceOf('Symfony\Component\Serializer\Encoder\DecoderInterface', $serializer);
}
/**
* @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/