From 489a8d8079d8bee72ee0c45dc572628f959a2776 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Fri, 15 Jul 2016 09:51:05 +0200 Subject: [PATCH 01/13] Added annotations and MetadataAwareNormalizer --- .../Serializer/Annotation/Accessor.php | 65 +++ .../Serializer/Annotation/Exclude.php | 32 ++ .../Serializer/Annotation/ExclusionPolicy.php | 50 +++ .../Serializer/Annotation/Expose.php | 32 ++ .../Serializer/Annotation/MaxDepth.php | 6 + .../Serializer/Annotation/ReadOnly.php | 44 ++ .../Serializer/Annotation/SerializedName.php | 40 ++ .../Component/Serializer/Annotation/Type.php | 52 +++ .../Serializer/Mapping/AttributeMetadata.php | 237 ++++++++++- .../Mapping/AttributeMetadataInterface.php | 99 +++++ .../Serializer/Mapping/ClassMetadata.php | 60 +++ .../Mapping/ClassMetadataInterface.php | 29 ++ .../Mapping/Loader/AnnotationLoader.php | 2 +- .../Mapping/Loader/BetterAnnotationLoader.php | 138 +++++++ .../Normalizer/AbstractNormalizer.php | 3 +- .../Normalizer/AbstractObjectNormalizer.php | 41 +- .../Normalizer/MetadataAwareNormalizer.php | 260 ++++++++++++ .../MetadataAwarePropertyTypeExtractor.php | 96 +++++ .../ReflectionPropertyAccess.php | 71 ++++ .../Tests/Annotation/AccessorTest.php | 59 +++ .../Tests/Annotation/ExcludeTest.php | 34 ++ .../Tests/Annotation/ExclusionPolicyTest.php | 53 +++ .../Tests/Annotation/ExposeTest.php | 34 ++ .../Tests/Annotation/ReadOnlyTest.php | 40 ++ .../Tests/Annotation/SerializedNameTest.php | 42 ++ .../Serializer/Tests/Annotation/TypeTest.php | 42 ++ .../Tests/Fixtures/AccessorDummy.php | 33 ++ .../Tests/Fixtures/CompositionChildDummy.php | 29 ++ .../Tests/Fixtures/CompositionDummy.php | 35 ++ .../Tests/Fixtures/ExcludeDummy.php | 26 ++ .../Fixtures/ExclusionPolicyAllDummy.php | 33 ++ .../Fixtures/ExclusionPolicyDefaultDummy.php | 32 ++ .../Fixtures/ExclusionPolicyNoneDummy.php | 33 ++ .../Serializer/Tests/Fixtures/ExposeDummy.php | 27 ++ .../Serializer/Tests/Fixtures/GroupsDummy.php | 32 ++ .../Tests/Fixtures/InheritanceDummy.php | 23 ++ .../Tests/Fixtures/InheritanceParentDummy.php | 13 + .../Tests/Fixtures/ReadOnlyClassDummy.php | 33 ++ .../Tests/Fixtures/ReadOnlyDummy.php | 26 ++ .../Tests/Fixtures/SerializedNameDummy.php | 29 ++ .../Tests/Mapping/AttributeMetadataTest.php | 128 +++++- .../Tests/Mapping/ClassMetadataTest.php | 26 ++ .../MetadataAwareNormalizerTest.php | 388 ++++++++++++++++++ .../Component/Serializer/Tests/autoload.php | 13 + .../Component/Serializer/phpunit.xml.dist | 2 +- 45 files changed, 2613 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Annotation/Accessor.php create mode 100644 src/Symfony/Component/Serializer/Annotation/Exclude.php create mode 100644 src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php create mode 100644 src/Symfony/Component/Serializer/Annotation/Expose.php create mode 100644 src/Symfony/Component/Serializer/Annotation/ReadOnly.php create mode 100644 src/Symfony/Component/Serializer/Annotation/SerializedName.php create mode 100644 src/Symfony/Component/Serializer/Annotation/Type.php create mode 100644 src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php create mode 100644 src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php create mode 100644 src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php create mode 100644 src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/CompositionChildDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ExcludeDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyDefaultDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyNoneDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ExposeDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/GroupsDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceParentDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/autoload.php diff --git a/src/Symfony/Component/Serializer/Annotation/Accessor.php b/src/Symfony/Component/Serializer/Annotation/Accessor.php new file mode 100644 index 0000000000000..330d681df8e42 --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/Accessor.php @@ -0,0 +1,65 @@ + + */ +final class Accessor +{ + /** + * @var string + */ + private $getter; + /** + * @var string + */ + private $setter; + + /** + * @param array $data + */ + public function __construct(array $data) + { + if (empty($data)) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + } + + foreach (array('getter', 'setter') as $parameter) { + if (!isset($data[$parameter]) || !$data[$parameter]) { + continue; + } + + if (!is_string($data[$parameter])) { + throw new InvalidArgumentException(sprintf('Parameter "%s" of annotation "%s" must be a string.', $parameter, get_class($this))); + } + + $this->$parameter = $data[$parameter]; + } + + if (null === $this->getter && null === $this->setter) { + throw new InvalidArgumentException(sprintf('Either option "getter" or "setter" must be given for annotation %s', get_class($this))); + } + } + + /** + * @return string + */ + public function getGetter() + { + return $this->getter; + } + + /** + * @return string + */ + public function getSetter() + { + return $this->setter; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/Exclude.php b/src/Symfony/Component/Serializer/Annotation/Exclude.php new file mode 100644 index 0000000000000..0dce14c8923c3 --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/Exclude.php @@ -0,0 +1,32 @@ + + */ +final class Exclude +{ + /** + * @param array $data + */ + public function __construct(array $data = array()) + { + if (!empty($data)) { + throw new InvalidArgumentException(sprintf('No parameter is allowed for annotation "%s".', get_class($this))); + } + } + + /** + * @return bool + */ + public function getValue() + { + return true; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php new file mode 100644 index 0000000000000..f147638008b61 --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php @@ -0,0 +1,50 @@ + + */ +final class ExclusionPolicy +{ + const NONE = 'NONE'; + const ALL = 'ALL'; + + /** + * @var string + */ + private $policy; + + /** + * @param array $data + */ + public function __construct(array $data) + { + if (!isset($data['value']) || !$data['value']) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + } + + if (!is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + } + + $this->policy = strtoupper($data['value']); + + if (self::NONE !== $this->policy && self::ALL !== $this->policy) { + throw new InvalidArgumentException('Exclusion policy must either be "ALL", or "NONE".'); + } + } + + /** + * @return string + */ + public function getPolicy() + { + return $this->policy; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/Expose.php b/src/Symfony/Component/Serializer/Annotation/Expose.php new file mode 100644 index 0000000000000..8c82111bc0d2d --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/Expose.php @@ -0,0 +1,32 @@ + + */ +final class Expose +{ + /** + * @param array $data + */ + public function __construct(array $data = array()) + { + if (!empty($data)) { + throw new InvalidArgumentException(sprintf('No parameter is allowed for annotation "%s".', get_class($this))); + } + } + + /** + * @return bool + */ + public function getValue() + { + return true; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/MaxDepth.php b/src/Symfony/Component/Serializer/Annotation/MaxDepth.php index a274c20d85283..742b7acb28cc9 100644 --- a/src/Symfony/Component/Serializer/Annotation/MaxDepth.php +++ b/src/Symfony/Component/Serializer/Annotation/MaxDepth.php @@ -28,6 +28,9 @@ class MaxDepth */ private $maxDepth; + /** + * @param array $data + */ public function __construct(array $data) { if (!isset($data['value'])) { @@ -41,6 +44,9 @@ public function __construct(array $data) $this->maxDepth = $data['value']; } + /** + * @return int + */ public function getMaxDepth() { return $this->maxDepth; diff --git a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php new file mode 100644 index 0000000000000..f4065f65ea37c --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php @@ -0,0 +1,44 @@ + + */ +final class ReadOnly +{ + /** + * @var bool + */ + private $readOnly; + + /** + * @param array $data + */ + public function __construct(array $data = array()) + { + if (empty($data) || !isset($data['value'])) { + $this->readOnly = true; + return; + } + + if (!is_bool($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a boolean.', get_class($this))); + } + + $this->readOnly = $data['value']; + } + + /** + * @return bool + */ + public function getReadOnly() + { + return $this->readOnly; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/SerializedName.php b/src/Symfony/Component/Serializer/Annotation/SerializedName.php new file mode 100644 index 0000000000000..e18a9c2fbdcc4 --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/SerializedName.php @@ -0,0 +1,40 @@ + + */ +final class SerializedName +{ + /** + * @var string + */ + private $name; + + /** + * @param array $data + */ + public function __construct(array $data) + { + if (!isset($data['value']) || !$data['value']) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + } + + if (!is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + } + + $this->name = $data['value']; + } + + public function getName() + { + return $this->name; + } +} diff --git a/src/Symfony/Component/Serializer/Annotation/Type.php b/src/Symfony/Component/Serializer/Annotation/Type.php new file mode 100644 index 0000000000000..4d04af0f47906 --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/Type.php @@ -0,0 +1,52 @@ + + */ +final class Type +{ + /** + * @var string + */ + private $type; + + /** + * @param array $data + */ + public function __construct(array $data) + { + if (!isset($data['value']) || !$data['value']) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + } + + if (!is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + } + + $type = $data['value']; + + if (false !== $pos = strpos($type, '\\')) { + // This is a referencet to a class + if ($pos !== 0) { + throw new InvalidArgumentException(sprintf('When referring to an class you you must begin the type with backslash (\\) you provided "%s" for annotation "%s".', $type, get_class($this))); + } + } + + $this->type = $data['value']; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index f0fd5f2a6ad48..4d1f56c29c710 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -25,6 +25,42 @@ class AttributeMetadata implements AttributeMetadataInterface */ public $name; + /** + * @var string + * + * @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 $accessorGetter; + + /** + * @var string + * + * @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 $accessorSetter; + + /** + * @var bool + * + * @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 $exclude; + + /** + * @var bool + * + * @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 $expose; + /** * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use @@ -41,6 +77,33 @@ class AttributeMetadata implements AttributeMetadataInterface */ public $maxDepth; + /** + * @var bool + * + * @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 $readOnly; + + /** + * @var string + * + * @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 $serializedName; + + /** + * @var string + * + * @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 $type; + public function __construct(string $name) { $this->name = $name; @@ -54,6 +117,86 @@ public function getName() return $this->name; } + /** + * @return string + */ + public function getAccessorGetter() + { + return $this->accessorGetter; + } + + /** + * @param string $accessorGetter + * + * @return AttributeMetadata + */ + public function setAccessorGetter($accessorGetter) + { + $this->accessorGetter = $accessorGetter; + + return $this; + } + + /** + * @return string + */ + public function getAccessorSetter() + { + return $this->accessorSetter; + } + + /** + * @param string $accessorSetter + * + * @return AttributeMetadata + */ + public function setAccessorSetter($accessorSetter) + { + $this->accessorSetter = $accessorSetter; + + return $this; + } + + /** + * @return boolean + */ + public function getExclude() + { + return $this->exclude; + } + + /** + * @param boolean $exclude + * + * @return AttributeMetadata + */ + public function setExclude($exclude) + { + $this->exclude = $exclude; + + return $this; + } + + /** + * @return boolean + */ + public function getExpose() + { + return $this->expose; + } + + /** + * @param boolean $expose + * + * @return AttributeMetadata + */ + public function setExpose($expose) + { + $this->expose = $expose; + + return $this; + } + /** * {@inheritdoc} */ @@ -88,6 +231,66 @@ public function getMaxDepth() return $this->maxDepth; } + /** + * @return boolean + */ + public function getReadOnly() + { + return $this->readOnly; + } + + /** + * @param boolean $readOnly + * + * @return AttributeMetadata + */ + public function setReadOnly($readOnly) + { + $this->readOnly = $readOnly; + + return $this; + } + + /** + * @return string + */ + public function getSerializedName() + { + return $this->serializedName; + } + + /** + * @param string $serializedName + * + * @return AttributeMetadata + */ + public function setSerializedName($serializedName) + { + $this->serializedName = $serializedName; + + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $type + * + * @return AttributeMetadata + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + /** * {@inheritdoc} */ @@ -98,9 +301,30 @@ public function merge(AttributeMetadataInterface $attributeMetadata) } // Overwrite only if not defined + if (null === $this->accessorGetter) { + $this->accessorGetter = $attributeMetadata->getAccessorGetter(); + } + if (null === $this->accessorSetter) { + $this->accessorSetter = $attributeMetadata->getAccessorSetter(); + } + if (null === $this->exclude) { + $this->exclude = $attributeMetadata->getExclude(); + } + if (null === $this->expose) { + $this->expose = $attributeMetadata->getExpose(); + } if (null === $this->maxDepth) { $this->maxDepth = $attributeMetadata->getMaxDepth(); } + if (null === $this->readOnly) { + $this->readOnly = $attributeMetadata->getReadOnly(); + } + if (null === $this->serializedName) { + $this->serializedName = $attributeMetadata->getSerializedName(); + } + if (null === $this->type) { + $this->type = $attributeMetadata->getType(); + } } /** @@ -110,6 +334,17 @@ public function merge(AttributeMetadataInterface $attributeMetadata) */ public function __sleep() { - return array('name', 'groups', 'maxDepth'); + return array( + 'name', + 'accessorGetter', + 'accessorSetter', + 'exclude', + 'expose', + 'groups', + 'maxDepth', + 'readOnly', + 'serializedName', + 'type', + ); } } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index d9a15d5ac0de5..e0b0c3943b3c4 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -19,6 +19,7 @@ * @internal * * @author Kévin Dunglas + * @author Tobias Nyholm */ interface AttributeMetadataInterface { @@ -29,6 +30,62 @@ interface AttributeMetadataInterface */ public function getName(); + /** + * Get the getter for this attribute. + * + * @return string|null + */ + public function getAccessorGetter(); + + /** + * Set the getter for this attribute. + * + * @param string|null + */ + public function setAccessorGetter($function); + + /** + * Get the setter for this attribute. + * + * @return string|null + */ + public function getAccessorSetter(); + + /** + * Set the setter for this attribute. + * + * @param string|null + */ + public function setAccessorSetter($function); + + /** + * True if this attribute should be excluded. + * + * @return boolean|null + */ + public function getExclude(); + + /** + * Set boolean value if this attribute should be excluded. + * + * @param bool $bool + */ + public function setExclude($bool); + + /** + * True if this attribute should be exposed. + * + * @return boolean|null + */ + public function getExpose(); + + /** + * Set boolean value if this attribute should be exposed. + * + * @param bool $bool + */ + public function setExpose($bool); + /** * Adds this attribute to the given group. * @@ -57,6 +114,48 @@ public function setMaxDepth($maxDepth); */ public function getMaxDepth(); + /** + * True if this attribute should be ignored when deserializing. + * + * @return boolean|null + */ + public function getReadOnly(); + + /** + * Set boolean value if this is a read only attribute. + * + * @param bool $bool + */ + public function setReadOnly($bool); + + /** + * Gets the serialized name of this attribute. + * + * @return string|null + */ + public function getSerializedName(); + + /** + * Set the name of this property after serialization. + * + * @param string $name + */ + public function setSerializedName($name); + + /** + * Gets the type (ie FQCN) of the attribute value. + * + * @return string|null + */ + public function getType(); + + /** + * Set the type of this attribute's value. A type could be Fully Qualified Class Name. + * + * @param string $fqcn + */ + public function setType($fqcn); + /** * Merges an {@see AttributeMetadataInterface} with in the current one. */ diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php index e1d474504c25b..e71ea55377213 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php @@ -25,6 +25,24 @@ class ClassMetadata implements ClassMetadataInterface */ public $name; + /** + * @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 $exclusionPolicy; + + /** + * @var bool + * + * @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 $readOnly; + /** * @var AttributeMetadataInterface[] * @@ -68,6 +86,46 @@ public function getName() return $this->name; } + /** + * @return string + */ + public function getExclusionPolicy() + { + return $this->exclusionPolicy; + } + + /** + * @param string $exclusionPolicy + * + * @return ClassMetadata + */ + public function setExclusionPolicy($exclusionPolicy) + { + $this->exclusionPolicy = $exclusionPolicy; + + return $this; + } + + /** + * @return boolean + */ + public function getReadOnly() + { + return $this->readOnly; + } + + /** + * @param boolean $readOnly + * + * @return ClassMetadata + */ + public function setReadOnly($readOnly) + { + $this->readOnly = $readOnly; + + return $this; + } + /** * {@inheritdoc} */ @@ -135,6 +193,8 @@ public function __sleep() { return array( 'name', + 'exclusionPolicy', + 'readOnly', 'attributesMetadata', 'classDiscriminatorMapping', ); diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php index 45ed03dfa1d90..b1627f63331d7 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php @@ -21,6 +21,7 @@ * @internal * * @author Kévin Dunglas + * @author Tobias Nyholm */ interface ClassMetadataInterface { @@ -31,6 +32,34 @@ interface ClassMetadataInterface */ public function getName(); + /** + * The default policy for excluding or exposing attributes. + * + * @return string|null + */ + public function getExclusionPolicy(); + + /** + * Set the policy for excluding attributes. + * + * @param string $policy + */ + public function setExclusionPolicy($policy); + + /** + * True if this class should be ignored when deserializing. + * + * @return boolean|null + */ + public function getReadOnly(); + + /** + * Set boolean value if this is a read only class. + * + * @param bool $bool + */ + public function setReadOnly($bool); + /** * Adds an {@link AttributeMetadataInterface}. */ diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index 0c195f671dad9..823b1c0533cd1 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -115,4 +115,4 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) return $loaded; } -} +} \ No newline at end of file diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php new file mode 100644 index 0000000000000..52a988f9acad9 --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Mapping\Loader; + +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\Serializer\Annotation; +use Symfony\Component\Serializer\Exception\MappingException; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; + +/** + * Annotation loader. + * + * @author Kévin Dunglas + * @author Tobias Nyholm + */ +class BetterAnnotationLoader implements LoaderInterface +{ + /** + * @var Reader + */ + private $reader; + + /** + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadataInterface $classMetadata) + { + $reflectionClass = $classMetadata->getReflectionClass(); + $className = $reflectionClass->name; + $loaded = false; + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + + foreach ($reflectionClass->getProperties() as $property) { + if (!isset($attributesMetadata[$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 $annotation) { + if ($annotation instanceof Annotation\Groups) { + foreach ($annotation->getGroups() as $group) { + $attributesMetadata[$property->name]->addGroup($group); + } + } elseif ($annotation instanceof Annotation\Accessor) { + $attributesMetadata[$property->name]->setAccessorGetter($annotation->getGetter()); + $attributesMetadata[$property->name]->setAccessorSetter($annotation->getSetter()); + } elseif ($annotation instanceof Annotation\Exclude) { + $attributesMetadata[$property->name]->setExclude($annotation->getValue()); + } elseif ($annotation instanceof Annotation\Expose) { + $attributesMetadata[$property->name]->setExpose($annotation->getValue()); + } elseif ($annotation instanceof Annotation\MaxDepth) { + $attributesMetadata[$property->name]->setMaxDepth($annotation->getMaxDepth()); + } elseif ($annotation instanceof Annotation\ReadOnly) { + $attributesMetadata[$property->name]->setReadOnly($annotation->getReadOnly()); + } elseif ($annotation instanceof Annotation\SerializedName) { + $attributesMetadata[$property->name]->setSerializedName($annotation->getName()); + } elseif ($annotation instanceof Annotation\Type) { + $attributesMetadata[$property->name]->setType($annotation->getType()); + } + + $loaded = true; + } + } + } + + foreach ($reflectionClass->getMethods() as $method) { + if ($method->getDeclaringClass()->name !== $className) { + continue; + } + + // Verify if method is an accessor or mutator for a property + $attributeName = $methodName = $method->name; + + if (isset($attributesMetadata[$attributeName])) { + // If we have a method with same name as a property, we ignore the method + continue; + } + + $attributesMetadata[$attributeName] = $attributeMetadata = new AttributeMetadata($attributeName); + $classMetadata->addAttributeMetadata($attributeMetadata); + + // Add default values for methods + $attributeMetadata->setAccessorGetter($methodName); + $attributeMetadata->setExclude(true); + $attributeMetadata->setReadOnly(true); + + foreach ($this->reader->getMethodAnnotations($method) as $annotation) { + if ($annotation instanceof Annotation\Groups) { + foreach ($annotation->getGroups() as $group) { + $attributeMetadata->addGroup($group); + } + } elseif ($annotation instanceof Annotation\Expose) { + $attributeMetadata->setExpose($annotation->getValue()); + $attributeMetadata->setExclude(!$annotation->getValue()); + } elseif ($annotation instanceof Annotation\MaxDepth) { + $attributeMetadata->setMaxDepth($annotation->getMaxDepth()); + } elseif ($annotation instanceof Annotation\SerializedName) { + $attributeMetadata->setSerializedName($annotation->getName()); + } + } + + $loaded = true; + } + + foreach ($this->reader->getClassAnnotations($reflectionClass) as $annotation) { + if ($annotation instanceof Annotation\ExclusionPolicy) { + $classMetadata->setExclusionPolicy($annotation->getPolicy()); + } elseif ($annotation instanceof Annotation\ReadOnly) { + $classMetadata->setReadOnly($annotation->getReadOnly()); + } + + $loaded = true; + } + + return $loaded; + } +} diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index bc6f68b7e88db..2a4bfd713b229 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -288,10 +288,11 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null * the denormalization process. * * @param object|array $data + * @param string $class * * @return array */ - protected function prepareForDenormalization($data) + protected function prepareForDenormalization($data, $class) { return (array) $data; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 524cb656c0e57..c64ccbb7d0127 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -108,7 +108,7 @@ public function normalize($object, $format = null, array $context = array()) $stack[$attribute] = $attributeValue; } - $data = $this->updateData($data, $attribute, $attributeValue); + $data = $this->updateData($data, $attribute, $attributeValue, $object); } foreach ($stack as $attribute => $attributeValue) { @@ -116,7 +116,7 @@ public function normalize($object, $format = null, array $context = array()) 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))); + $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute)), $object); } return $data; @@ -238,7 +238,7 @@ public function denormalize($data, $class, $format = null, array $context = arra } $allowedAttributes = $this->getAllowedAttributes($class, $context, true); - $normalizedData = $this->prepareForDenormalization($data); + $normalizedData = $this->prepareForDenormalization($data, $class); $extraAttributes = array(); $reflectionClass = new \ReflectionClass($class); @@ -272,6 +272,19 @@ public function denormalize($data, $class, $format = null, array $context = arra return $object; } + /** + * {@inheritdoc} + */ + protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array()) + { + if (preg_match('/^(get|is|has|set)(.+)$/i', $attribute, $matches)) { + // We do not allow access to accessors or mutators directly + return false; + } + + return !in_array($attribute, $this->ignoredAttributes); + } + /** * Sets attribute value. * @@ -300,6 +313,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, } $expectedTypes = array(); + /** @var Type $type */ foreach ($types as $type) { if (null === $data && $type->isNullable()) { return; @@ -334,6 +348,24 @@ private function validateAndDenormalize(string $currentClass, string $attribute, if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { return $this->serializer->denormalize($data, $class, $format, $childContext); } + } elseif (Type::BUILTIN_TYPE_ARRAY === $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)); + } + + if (!is_array($data) && !$data instanceof \Traversable) { + throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because the value is "%s", expected array', $attribute, $class, gettype($data))); + } + + $denormalizedData = []; + $childClass = $type->getCollectionValueType()->getClassName(); + foreach ($data as $d) { + if ($this->serializer->supportsDenormalization($d, $childClass, $format)) { + $denormalizedData[] = $this->serializer->denormalize($d, $childClass, $format, $context); + } + } + + return $denormalizedData; } // JSON only has a Number type corresponding to both int and float PHP types. @@ -399,8 +431,9 @@ private function getTypes(string $currentClass, string $attribute) * Sets an attribute and apply the name converter if necessary. * * @param mixed $attributeValue + * @internal */ - private function updateData(array $data, string $attribute, $attributeValue): array + protected function updateData(array $data, string $attribute, $attributeValue): array { if ($this->nameConverter) { $attribute = $this->nameConverter->normalize($attribute); diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php new file mode 100644 index 0000000000000..691f7e2578f9b --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -0,0 +1,260 @@ + + * + * 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\PropertyAccessorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\Serializer\Annotation\ExclusionPolicy; +use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\PropertyManager\ReflectionPropertyAccess; + +/** + * Converts between objects and arrays using the Reflection and respect the metadata. + * + * @author Tobias Nyholm + */ +class MetadataAwareNormalizer extends AbstractObjectNormalizer +{ + /** + * @var ReflectionPropertyAccess + */ + protected $propertyAccessor; + + public function __construct( + ClassMetadataFactoryInterface $classMetadataFactory, + NameConverterInterface $nameConverter = null, + PropertyAccessorInterface $propertyAccessor = null, + PropertyTypeExtractorInterface $propertyTypeExtractor = null) + { + parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor); + + $this->propertyAccessor = new ReflectionPropertyAccess(); + } + + /** + * {@inheritdoc} + */ + protected function extractAttributes($object, $format = null, array $context = array()) + { + // If not using groups, detect manually + $attributes = array(); + + // methods + $reflClass = new \ReflectionClass($object); + foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { + if ( + $reflMethod->getNumberOfRequiredParameters() !== 0 || + $reflMethod->isStatic() || + $reflMethod->isConstructor() || + $reflMethod->isDestructor() + ) { + continue; + } + + $attributeName = $reflMethod->name; + if (null !== $attributeName && $this->isAllowedAttribute($object, $attributeName, $format, $context)) { + $attributes[$attributeName] = true; + } + } + + // properties + foreach ($reflClass->getProperties() as $reflProperty) { + 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 = array()) + { + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($object); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + + if (null !== $function = $attributeMetadata[$attribute]->getAccessorGetter()) { + return $object->$function(); + } + + return $this->propertyAccessor->getValue($object, $attribute); + } + + /** + * {@inheritdoc} + */ + protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array()) + { + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($object); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + + if (null !== $function = $attributeMetadata[$attribute]->getAccessorSetter()) { + return $object->$function($value); + } + + return $this->propertyAccessor->setValue($object, $attribute, $value); + } + + /** + * 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()) + { + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($classOrObject); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + + if (true === $attributeMetadata[$attribute]->getExclude()) { + return false; + } + + if (true === $attributeMetadata[$attribute]->getExpose()) { + return true; + } + + if ($classMetadata->getExclusionPolicy() === ExclusionPolicy::ALL) { + return false; + } + + // If no exclusion policy or ExclusionPolicy::NONE + return parent::isAllowedAttribute($classOrObject, $attribute, $format, $context); + } + + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + if (!is_object($data)) { + return false; + } + + if (!$this->classMetadataFactory) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) + { + if ( + isset($context[AbstractNormalizer::OBJECT_TO_POPULATE]) && + is_object($context[AbstractNormalizer::OBJECT_TO_POPULATE]) && + $context[AbstractNormalizer::OBJECT_TO_POPULATE] instanceof $class + ) { + $object = $context[AbstractNormalizer::OBJECT_TO_POPULATE]; + unset($context[AbstractNormalizer::OBJECT_TO_POPULATE]); + + return $object; + } + + $reflectionClass = new \ReflectionClass($class); + $constructor = $reflectionClass->getConstructor(); + if (!$constructor) { + return new $class(); + } + + return $reflectionClass->newInstanceWithoutConstructor(); + } + + /** + * Update data for normalization. + * + * {@inheritdoc} + */ + protected function updateData(array $data, $attribute, $attributeValue, $object) + { + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($object); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + + if (null !== $name = $attributeMetadata[$attribute]->getSerializedName()) { + $attribute = $name; + } elseif ($this->nameConverter) { + $attribute = $this->nameConverter->normalize($attribute); + } + + $data[$attribute] = $attributeValue; + + return $data; + } + + /** + * Prepare data for denormalization. + * + * {@inheritdoc} + */ + protected function prepareForDenormalization($data, $class) + { + $preparedData = []; + $data = (array) $data; + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($class); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + $classReadOnly = true === $classMetadata->getReadOnly(); + + $validSerializedKeys = []; + foreach ($attributeMetadata as $attributeName => $metadata) { + $attributeReadOnly = $metadata->getReadOnly(); + if ($attributeReadOnly === true || ($classReadOnly && $attributeReadOnly !== false)) { + // This is not a valid key + continue; + } + + // Get the special serialized name + if (null === $serializedName = $metadata->getSerializedName()) { + $serializedName = $attributeName; + } + + $serializedAttributeName = $attributeName; + if ($this->nameConverter) { + // We want to convert this no matter if we got a serialized name in the meta or not + $serializedName = $this->nameConverter->normalize($serializedName); + $serializedAttributeName = $this->nameConverter->normalize($attributeName); + } + + $validSerializedKeys[$serializedName] = $serializedAttributeName; + } + + $validSerializedKeyNames = array_keys($validSerializedKeys); + foreach ($data as $serializedKeyName => $value) { + if (in_array($serializedKeyName, $validSerializedKeyNames)) { + // Replace with the keys for the serialized attribute + $preparedData[$validSerializedKeys[$serializedKeyName]] = $value; + } + } + + // Assert: The keys in this array are the attribute name passed throw the nameConverter::normalize + return $preparedData; + } +} diff --git a/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php new file mode 100644 index 0000000000000..f13fc7411342d --- /dev/null +++ b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php @@ -0,0 +1,96 @@ + + */ +class MetadataAwarePropertyTypeExtractor implements PropertyTypeExtractorInterface +{ + /** + * @var ClassMetadataFactoryInterface + */ + private $classMetadataFactory; + + /** + * @var PropertyTypeExtractorInterface + */ + private $propertyTypeExtractor; + + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory, PropertyTypeExtractorInterface $propertyTypeExtractor = null) + { + $this->classMetadataFactory = $classMetadataFactory; + $this->propertyTypeExtractor = $propertyTypeExtractor; + } + + public function getTypes($class, $property, array $context = array()) + { + /** @var ClassMetadataInterface $classMetadata */ + $classMetadata = $this->classMetadataFactory->getMetadataFor($class); + $attributeMetadata = $classMetadata->getAttributesMetadata(); + + if (null !== $string = $attributeMetadata[$property]->getType()) { + return array($this->convertStringToType($string)); + } + + + if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class, $property)) { + return null; + } + + return $types; + } + + /** + * Convert a plain string to a Type + * + * @param string $doctype + */ + private function convertStringToType($docType) + { + if ($collection = '[]' === substr($docType, -2)) { + $docType = substr($docType, 0, -2); + } + + list($phpType, $class) = $this->getPhpTypeAndClass($docType); + $array = 'array' === $docType; + + if ($collection || $array) { + if ($array || 'mixed' === $docType) { + $collectionKeyType = null; + $collectionValueType = null; + } else { + $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT); + $collectionValueType = new Type($phpType, false, $class); + } + + return new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, $collectionKeyType, $collectionValueType); + } + + return new Type($phpType, true, $class); + } + + /** + * Gets an array containing the PHP type and the class. + * + * @param string $docType + * + * @return array + */ + private function getPhpTypeAndClass($docType) + { + if (in_array($docType, Type::$builtinTypes)) { + return array($docType, null); + } + + return array('object', substr($docType, 1)); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php b/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php new file mode 100644 index 0000000000000..00bb09ff502a2 --- /dev/null +++ b/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php @@ -0,0 +1,71 @@ + + */ +class ReflectionPropertyAccess +{ + /** + * @param object $object + * @param string $propertyName + * @param mixed $value + */ + public function setValue($object, $propertyName, $value) + { + $reflectionProperty = $this->getReflectionProperty($object, $propertyName); + $reflectionProperty->setValue($object, $value); + } + + /** + * @param object $object + * @param string $propertyName + * + * @return mixed + */ + public function getValue($object, $propertyName) + { + $reflectionProperty = $this->getReflectionProperty($object, $propertyName); + + return $reflectionProperty->getValue($object); + } + + /** + * @param mixed $objectOrClass + * @param string $propertyName + * + * @return \ReflectionProperty + */ + private function getReflectionProperty($objectOrClass, $propertyName) + { + $reflectionClass = new \ReflectionClass($objectOrClass); + + if (!$reflectionClass->hasProperty($propertyName)) { + if (false === $parent = get_parent_class($objectOrClass)) { + if (is_object($objectOrClass)) { + $objectOrClass = get_class($objectOrClass); + } + throw new LogicException(sprintf('There is no property "%s" on class "%s"', $propertyName, $objectOrClass)); + } + + try { + return $this->getReflectionProperty($parent, $propertyName); + } catch (LogicException $e) { + if (is_object($objectOrClass)) { + $objectOrClass = get_class($objectOrClass); + } + throw new LogicException(sprintf('There is no property "%s" on class "%s"', $propertyName, $objectOrClass), 0, $e); + } + } + + $reflectionProperty = $reflectionClass->getProperty($propertyName); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php new file mode 100644 index 0000000000000..cbe83eb303583 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\Accessor; + +/** + * @author Tobias Nyholm + */ +class AccessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testNoParameter() + { + new Accessor(array()); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameter() + { + new Accessor(array('value' => 'Foobar')); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidSetterParameter() + { + new Accessor(array('setter' => new \stdClass())); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidGetterParameter() + { + new Accessor(array('getter' => array())); + } + + public function testAccessorParameters() + { + $accessor = new Accessor(array('setter' => 'Foo', 'getter' => 'Bar')); + $this->assertEquals('Bar', $accessor->getGetter()); + $this->assertEquals('Foo', $accessor->getSetter()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php new file mode 100644 index 0000000000000..a1817cf7f853d --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\Exclude; + +/** + * @author Tobias Nyholm + */ +class ExcludeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameter() + { + new Exclude(array('value' => 'Foobar')); + } + + public function testExclude() + { + $exclude = new Exclude(); + $this->assertTrue($exclude->getValue()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php new file mode 100644 index 0000000000000..7c74b44fce863 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\ExclusionPolicy; + +/** + * @author Tobias Nyholm + */ +class ExclusionPolicyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testNoParameter() + { + new ExclusionPolicy(array()); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameter() + { + new ExclusionPolicy(array('value' => 'Foobar')); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameterType() + { + new ExclusionPolicy(array('value' => new \stdClass())); + } + + public function testExclusionPolicy() + { + $policy = new ExclusionPolicy(array('value' => 'all')); + $this->assertEquals(ExclusionPolicy::ALL, $policy->getPolicy()); + + $policy = new ExclusionPolicy(array('value' => 'none')); + $this->assertEquals(ExclusionPolicy::NONE, $policy->getPolicy()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php new file mode 100644 index 0000000000000..5895620db1857 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\Expose; + +/** + * @author Tobias Nyholm + */ +class ExposeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameter() + { + new Expose(array('value' => 'Foobar')); + } + + public function testExpose() + { + $expose = new Expose(); + $this->assertTrue($expose->getValue()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php new file mode 100644 index 0000000000000..dc5c4be7b3f85 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\ReadOnly; + +/** + * @author Tobias Nyholm + */ +class ReadOnlyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameter() + { + new ReadOnly(array('value' => 'Foobar')); + } + + public function testReadOnly() + { + $readOnly = new ReadOnly(); + $this->assertTrue($readOnly->getReadOnly()); + + $readOnly = new ReadOnly(array('value' => true)); + $this->assertTrue($readOnly->getReadOnly()); + + $readOnly = new ReadOnly(array('value' => false)); + $this->assertFalse($readOnly->getReadOnly()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php new file mode 100644 index 0000000000000..e41a9cb7acefc --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\SerializedName; + +/** + * @author Tobias Nyholm + */ +class SerializedNameTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testNoParameter() + { + new SerializedName(array()); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameterType() + { + new SerializedName(array('value' => new \stdClass())); + } + + public function testSerializedName() + { + $serializedName = new SerializedName(array('value' => 'Foobar')); + $this->assertEquals('Foobar', $serializedName->getName()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php new file mode 100644 index 0000000000000..72c172531d9f8 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use Symfony\Component\Serializer\Annotation\Type; + +/** + * @author Tobias Nyholm + */ +class TypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testNoParameter() + { + new Type(array()); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + */ + public function testInvalidParameterType() + { + new Type(array('value' => new \stdClass())); + } + + public function testType() + { + $type = new Type(array('value' => 'Foobar')); + $this->assertEquals('Foobar', $type->getType()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php new file mode 100644 index 0000000000000..afb0bc76cdc2f --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php @@ -0,0 +1,33 @@ +model = $model.'_setter'; + + return $this; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionChildDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionChildDummy.php new file mode 100644 index 0000000000000..97f1af83be310 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionChildDummy.php @@ -0,0 +1,29 @@ + + */ +class CompositionChildDummy +{ + /** + * @Serializer\SerializedName("super_model") + */ + public $model; + + public $carSize; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->carSize = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php new file mode 100644 index 0000000000000..b9cbf7873a2c3 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php @@ -0,0 +1,35 @@ + + */ +class CompositionDummy +{ + public $name; + + /** + * @Serializer\Type("\Symfony\Component\Serializer\Tests\Fixtures\CompositionChildDummy") + */ + private $child; + + + public function __construct($withValues = false) + { + if ($withValues) { + $this->name = 'Foobar'; + $this->child = new CompositionChildDummy(true); + } + } + + /** + * @return Car + */ + public function getChild() + { + return $this->child; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExcludeDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExcludeDummy.php new file mode 100644 index 0000000000000..5bf44d144206d --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExcludeDummy.php @@ -0,0 +1,26 @@ + + */ +class ExcludeDummy +{ + /** + * @Serializer\Exclude + */ + public $model; + + public $size; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php new file mode 100644 index 0000000000000..c15005b241bad --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php @@ -0,0 +1,33 @@ + + */ +class ExclusionPolicyAllDummy +{ + /** + * @Serializer\Exclude + */ + public $model; + + /** + * @Serializer\Expose + */ + public $size; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyDefaultDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyDefaultDummy.php new file mode 100644 index 0000000000000..1113574e9019e --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyDefaultDummy.php @@ -0,0 +1,32 @@ + + */ +class ExclusionPolicyDefaultDummy +{ + /** + * @Serializer\Exclude + */ + public $model; + + /** + * @Serializer\Expose + */ + public $size; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyNoneDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyNoneDummy.php new file mode 100644 index 0000000000000..1d6df4417a1af --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyNoneDummy.php @@ -0,0 +1,33 @@ + + */ +class ExclusionPolicyNoneDummy +{ + /** + * @Serializer\Exclude + */ + public $model; + + /** + * @Serializer\Expose + */ + public $size; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExposeDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExposeDummy.php new file mode 100644 index 0000000000000..911fc1bf50111 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExposeDummy.php @@ -0,0 +1,27 @@ + + */ +class ExposeDummy +{ + /** + * @Serializer\Expose + */ + public $model; + + public $size; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/GroupsDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/GroupsDummy.php new file mode 100644 index 0000000000000..e1d1354f3b361 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/GroupsDummy.php @@ -0,0 +1,32 @@ + + */ +class GroupsDummy +{ + /** + * @Serializer\Groups({"First", "Second"}) + */ + public $model; + + /** + * @Serializer\Groups({"First"}) + */ + public $size; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceDummy.php new file mode 100644 index 0000000000000..a372e9bdf92a5 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceDummy.php @@ -0,0 +1,23 @@ + + */ +class InheritanceDummy extends InheritanceParentDummy +{ + public $name; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->name = 'val_name'; + $this->age = 'val_age'; + } + } + + +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceParentDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceParentDummy.php new file mode 100644 index 0000000000000..79e84587e861c --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/InheritanceParentDummy.php @@ -0,0 +1,13 @@ + + */ +class InheritanceParentDummy +{ + public $age; +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php new file mode 100644 index 0000000000000..eda46a67a5311 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php @@ -0,0 +1,33 @@ + + */ +class ReadOnlyClassDummy +{ + /** + * @Serializer\ReadOnly(false) + */ + public $model; + + public $size; + + /** + * @Serializer\ReadOnly() + */ + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyDummy.php new file mode 100644 index 0000000000000..945180cc2bb7f --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyDummy.php @@ -0,0 +1,26 @@ + + */ +class ReadOnlyDummy +{ + /** + * @Serializer\ReadOnly() + */ + public $model; + + public $size; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->size = 'val_size'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php new file mode 100644 index 0000000000000..5b2dbefd1b9d0 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php @@ -0,0 +1,29 @@ + + */ +class SerializedNameDummy +{ + /** + * @Serializer\SerializedName("super_model") + */ + public $model; + + public $carSize; + + public $color; + + public function __construct($withValues = false) + { + if ($withValues) { + $this->model = 'val_model'; + $this->carSize = 'val_size'; + $this->color = 'val_color'; + } + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php index 16ebcb1aac8dc..06ab1d203a1f1 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php @@ -16,6 +16,7 @@ /** * @author Kévin Dunglas + * @author Tobias Nyholm */ class AttributeMetadataTest extends TestCase { @@ -31,6 +32,43 @@ public function testGetName() $this->assertEquals('name', $attributeMetadata->getName()); } + public function testAccessor() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getAccessorGetter()); + $this->assertNull($attributeMetadata->getAccessorSetter()); + + $attributeMetadata->setAccessorGetter('getter'); + $this->assertEquals('getter', $attributeMetadata->getAccessorGetter()); + + $attributeMetadata->setAccessorSetter('setter'); + $this->assertEquals('setter', $attributeMetadata->getAccessorSetter()); + } + + public function testExclude() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getExclude()); + + $attributeMetadata->setExclude(true); + $this->assertTrue($attributeMetadata->getExclude()); + + $attributeMetadata->setExclude(false); + $this->assertFalse($attributeMetadata->getExclude()); + } + + public function testExpose() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getExpose()); + + $attributeMetadata->setExpose(true); + $this->assertTrue($attributeMetadata->getExpose()); + + $attributeMetadata->setExpose(false); + $this->assertFalse($attributeMetadata->getExpose()); + } + public function testGroups() { $attributeMetadata = new AttributeMetadata('group'); @@ -44,11 +82,43 @@ public function testGroups() public function testMaxDepth() { $attributeMetadata = new AttributeMetadata('name'); - $attributeMetadata->setMaxDepth(69); + $this->assertNull($attributeMetadata->getMaxDepth()); + $attributeMetadata->setMaxDepth(69); $this->assertEquals(69, $attributeMetadata->getMaxDepth()); } + public function testReadOnly() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getReadOnly()); + + $attributeMetadata->setReadOnly(true); + $this->assertTrue($attributeMetadata->getReadOnly()); + + $attributeMetadata->setReadOnly(false); + $this->assertFalse($attributeMetadata->getReadOnly()); + } + + public function testSerializedName() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getSerializedName()); + + $serializedName = 'foobar'; + $attributeMetadata->setSerializedName($serializedName); + $this->assertEquals($serializedName, $attributeMetadata->getSerializedName()); + } + public function testType() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertNull($attributeMetadata->getType()); + + $type = 'foobar'; + $attributeMetadata->setType($type); + $this->assertEquals($type, $attributeMetadata->getType()); + } + public function testMerge() { $attributeMetadata1 = new AttributeMetadata('a1'); @@ -58,12 +128,68 @@ public function testMerge() $attributeMetadata2 = new AttributeMetadata('a2'); $attributeMetadata2->addGroup('a'); $attributeMetadata2->addGroup('c'); + $attributeMetadata2->setAccessorGetter('getter'); + $attributeMetadata2->setAccessorSetter('setter'); + $attributeMetadata2->setExclude(true); + $attributeMetadata2->setExpose(false); $attributeMetadata2->setMaxDepth(2); + $attributeMetadata2->setReadOnly(true); + $attributeMetadata2->setSerializedName('serialized_name'); + $attributeMetadata2->setType('type'); + + $attributeMetadata1->merge($attributeMetadata2); + + $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); + $this->assertEquals('getter', $attributeMetadata1->getAccessorGetter()); + $this->assertEquals('setter', $attributeMetadata1->getAccessorSetter()); + $this->assertEquals(true, $attributeMetadata1->getExclude()); + $this->assertEquals(false, $attributeMetadata1->getExpose()); + $this->assertEquals(2, $attributeMetadata1->getMaxDepth()); + $this->assertEquals(true, $attributeMetadata1->getReadOnly()); + $this->assertEquals('serialized_name', $attributeMetadata1->getSerializedName()); + $this->assertEquals('type', $attributeMetadata1->getType()); + } + + /** + * Exsisting values of $attributeMetadata1 should not be overwritten. + */ + public function testMergeNoOverwrite() + { + $attributeMetadata1 = new AttributeMetadata('a1'); + $attributeMetadata1->addGroup('a'); + $attributeMetadata1->addGroup('b'); + $attributeMetadata1->setAccessorGetter('getter'); + $attributeMetadata1->setAccessorSetter('setter'); + $attributeMetadata1->setExclude(true); + $attributeMetadata1->setExpose(false); + $attributeMetadata1->setMaxDepth(2); + $attributeMetadata1->setReadOnly(true); + $attributeMetadata1->setSerializedName('serialized_name'); + $attributeMetadata1->setType('type'); + + $attributeMetadata2 = new AttributeMetadata('a2'); + $attributeMetadata2->addGroup('a'); + $attributeMetadata2->addGroup('c'); + $attributeMetadata2->setAccessorGetter('getter2'); + $attributeMetadata2->setAccessorSetter('setter2'); + $attributeMetadata2->setExclude(false); + $attributeMetadata2->setExpose(true); + $attributeMetadata2->setMaxDepth(3); + $attributeMetadata2->setReadOnly(false); + $attributeMetadata2->setSerializedName('serialized_name2'); + $attributeMetadata2->setType('type2'); $attributeMetadata1->merge($attributeMetadata2); $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); + $this->assertEquals('getter', $attributeMetadata1->getAccessorGetter()); + $this->assertEquals('setter', $attributeMetadata1->getAccessorSetter()); + $this->assertEquals(true, $attributeMetadata1->getExclude()); + $this->assertEquals(false, $attributeMetadata1->getExpose()); $this->assertEquals(2, $attributeMetadata1->getMaxDepth()); + $this->assertEquals(true, $attributeMetadata1->getReadOnly()); + $this->assertEquals('serialized_name', $attributeMetadata1->getSerializedName()); + $this->assertEquals('type', $attributeMetadata1->getType()); } public function testSerialize() diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php index f2db1c51f6fcd..78c555147627f 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php @@ -16,6 +16,7 @@ /** * @author Kévin Dunglas + * @author Tobias Nyholm */ class ClassMetadataTest extends TestCase { @@ -64,10 +65,35 @@ public function testMerge() $this->assertEquals(array('a1' => $ac1), $classMetadata2->getAttributesMetadata()); } + public function testExclusionPolicy() + { + $policy = 'foo'; + $classMetadata = new ClassMetadata('a'); + $this->assertNull($classMetadata->getExclusionPolicy()); + + $classMetadata->setExclusionPolicy($policy); + $this->assertEquals($policy, $classMetadata->getExclusionPolicy()); + } + + public function testReadOnly() + { + $classMetadata = new ClassMetadata('a'); + $this->assertNull($classMetadata->getReadOnly()); + + $classMetadata->setReadOnly(true); + $this->assertTrue($classMetadata->getReadOnly()); + + $classMetadata->setReadOnly(false); + $this->assertFalse($classMetadata->getReadOnly()); + } + public function testSerialize() { $classMetadata = new ClassMetadata('a'); + $classMetadata->setExclusionPolicy('foo'); + $classMetadata->setReadOnly(true); + $a1 = $this->getMockBuilder('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface')->getMock(); $a1->method('getName')->willReturn('b1'); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php new file mode 100644 index 0000000000000..397a89ffd83f8 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use Doctrine\Common\Annotations\AnnotationReader; +use Symfony\Component\Serializer\Mapping\Loader\BetterAnnotationLoader; +use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\MetadataAwareNormalizer; +use Symfony\Component\Serializer\PropertyManager\MetadataAwarePropertyTypeExtractor; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\AccessorDummy; +use Symfony\Component\Serializer\Tests\Fixtures\CompositionChildDummy; +use Symfony\Component\Serializer\Tests\Fixtures\CompositionDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ExcludeDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ExclusionPolicyAllDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ExclusionPolicyDefaultDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ExclusionPolicyNoneDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ExposeDummy; +use Symfony\Component\Serializer\Tests\Fixtures\GroupsDummy; +use Symfony\Component\Serializer\Tests\Fixtures\InheritanceDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ReadOnlyClassDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ReadOnlyDummy; +use Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; + +/** + * @author Tobias Nyholm + */ +class MetadataAwareNormalizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var MetadataAwareNormalizer + */ + private $normalizer; + /** + * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + + protected function setUp() + { + $this->serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); + $this->normalizer = new MetadataAwareNormalizer($classMetadataFactory, null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $this->normalizer->setSerializer($this->serializer); + } + + public function testAccessorNormalize() + { + $data = $this->normalizer->normalize(new AccessorDummy()); + + $this->assertTrue(isset($data['model'])); + $this->assertEquals('getModel', $data['model']); + } + + public function testAccessorDenormalize() + { + $data = ['model' => 'model_val']; + $obj = $this->normalizer->denormalize($data, AccessorDummy::class); + + $this->assertEquals('model_val_setter', $obj->model); + } + + public function testCompositionNormalize() + { + $serializer = new MicroSerializer($this->normalizer); + $this->normalizer->setSerializer($serializer); + + $data = $this->normalizer->normalize(new CompositionDummy(true)); + + $this->assertTrue(isset($data['child'])); + $this->assertTrue(isset($data['child']['color'])); + $this->assertEquals('val_color', $data['child']['color']); + $this->assertTrue(isset($data['name'])); + } + + public function testCompositionDenormalize() + { + $serializer = new MicroSerializer($this->normalizer); + $this->normalizer->setSerializer($serializer); + + $data = json_decode('{"name":"Foobar","child":{"super_model":"val_model","car_size":"val_size","color":"val_color"}}', true); + $obj = $this->normalizer->denormalize($data, CompositionDummy::class); + + $this->assertEquals('Foobar', $obj->name); + $child = $obj->getChild(); + $this->assertInstanceOf(CompositionChildDummy::class, $child); + $this->assertEquals('val_color', $child->color); + } + + public function testInheritanceNormalize() + { + $data = $this->normalizer->normalize(new InheritanceDummy(true)); + + $this->assertTrue(isset($data['name'])); + $this->assertTrue(isset($data['age'])); + } + + public function testInheritanceDenormalize() + { + $data = ['name' => 'Foo', 'age' => 'Bar']; + $obj = $this->normalizer->denormalize($data, InheritanceDummy::class); + + $this->assertEquals('Foo', $obj->name); + $this->assertEquals('Bar', $obj->age); + } + + public function testExcludeNormalize() + { + $data = $this->normalizer->normalize(new ExcludeDummy(true)); + + $this->assertFalse(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + } + + public function testExcludeDenormalize() + { + $data = ['model' => 'model_value', 'size' => 'size_value']; + $obj = $this->normalizer->denormalize($data, ExcludeDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals('size_value', $obj->size); + } + + public function testExclusionPolicyNormalizeDefault() + { + $data = $this->normalizer->normalize(new ExclusionPolicyDefaultDummy(true)); + + $this->assertFalse(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertTrue(isset($data['color'])); + } + + public function testExclusionPolicyNormalizeNone() + { + $data = $this->normalizer->normalize(new ExclusionPolicyNoneDummy(true)); + + $this->assertFalse(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertTrue(isset($data['color'])); + } + + public function testExclusionPolicyNormalizeAll() + { + $data = $this->normalizer->normalize(new ExclusionPolicyAllDummy(true)); + + $this->assertFalse(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertFalse(isset($data['color'])); + } + + public function testExclusionPolicyDenormalizeDefault() + { + $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; + $obj = $this->normalizer->denormalize($data, ExclusionPolicyDefaultDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals('size_value', $obj->size); + $this->assertEquals('color_value', $obj->color); + } + + public function testExclusionPolicyDenormalizeNone() + { + $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; + $obj = $this->normalizer->denormalize($data, ExclusionPolicyNoneDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals('size_value', $obj->size); + $this->assertEquals('color_value', $obj->color); + } + + public function testExclusionPolicyDenormalizeAll() + { + $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; + $obj = $this->normalizer->denormalize($data, ExclusionPolicyAllDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals('size_value', $obj->size); + $this->assertEquals(null, $obj->color); + } + + public function testExposeNormalize() + { + $data = $this->normalizer->normalize(new ExposeDummy(true)); + + $this->assertTrue(isset($data['model'])); + $this->assertFalse(isset($data['size'])); + } + + public function testExposeDenormalize() + { + $data = ['model' => 'model_value', 'size' => 'size_value']; + $obj = $this->normalizer->denormalize($data, ExposeDummy::class); + + $this->assertEquals('model_value', $obj->model); + $this->assertEquals(null, $obj->size); + } + + public function testGroupsNormalize() + { + $data = $this->normalizer->normalize(new GroupsDummy(true), null, ['groups' => ['First']]); + $this->assertTrue(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertFalse(isset($data['color'])); + + $data = $this->normalizer->normalize(new GroupsDummy(true), null, ['groups' => ['Second']]); + $this->assertTrue(isset($data['model'])); + $this->assertFalse(isset($data['size'])); + $this->assertFalse(isset($data['color'])); + + $data = $this->normalizer->normalize(new GroupsDummy(true), null, ['groups' => ['First', 'Second']]); + $this->assertTrue(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertFalse(isset($data['color'])); + + $data = $this->normalizer->normalize(new GroupsDummy(true), null, ['groups' => []]); + $this->assertFalse(isset($data['model'])); + $this->assertFalse(isset($data['size'])); + $this->assertFalse(isset($data['color'])); + } + + public function testGroupsDenormalize() + { + $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; + + $obj = $this->normalizer->denormalize($data, GroupsDummy::class, null, ['groups' => ['First']]); + $this->assertEquals('model_value', $obj->model); + $this->assertEquals('size_value', $obj->size); + $this->assertEquals(null, $obj->color); + + $obj = $this->normalizer->denormalize($data, GroupsDummy::class, null, ['groups' => ['Second']]); + $this->assertEquals('model_value', $obj->model); + $this->assertEquals(null, $obj->size); + $this->assertEquals(null, $obj->color); + + $obj = $this->normalizer->denormalize($data, GroupsDummy::class, null, ['groups' => ['First', 'Second']]); + $this->assertEquals('model_value', $obj->model); + $this->assertEquals('size_value', $obj->size); + $this->assertEquals(null, $obj->color); + + $obj = $this->normalizer->denormalize($data, GroupsDummy::class, null, ['groups' => []]); + $this->assertEquals(null, $obj->model); + $this->assertEquals(null, $obj->size); + $this->assertEquals(null, $obj->color); + } + + public function testReadOnlyNormalize() + { + $data = $this->normalizer->normalize(new ReadOnlyDummy(true)); + + $this->assertTrue(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + } + + public function testReadOnlyDenormalize() + { + $data = ['model' => 'model_value', 'size' => 'size_value']; + $obj = $this->normalizer->denormalize($data, ReadOnlyDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals('size_value', $obj->size); + } + + public function testReadOnlyNormalizeClass() + { + $data = $this->normalizer->normalize(new ReadOnlyClassDummy(true)); + + $this->assertTrue(isset($data['model'])); + $this->assertTrue(isset($data['size'])); + $this->assertTrue(isset($data['color'])); + } + + public function testReadOnlyDenormalizeClass() + { + $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; + $obj = $this->normalizer->denormalize($data, ReadOnlyClassDummy::class); + + $this->assertEquals('model_value', $obj->model); + $this->assertEquals(null, $obj->size); + $this->assertEquals(null, $obj->color); + } + + public function testSerializedNameNormalize() + { + $data = $this->normalizer->normalize(new SerializedNameDummy(true)); + + $this->assertTrue(isset($data['super_model'])); + $this->assertTrue(isset($data['carSize'])); + $this->assertTrue(isset($data['color'])); + } + + public function testSerializedNameDenormalize() + { + $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer->setSerializer($serializer); + + $data = ['super_model' => 'model_val', 'car_size' => 'size_val', 'color' => 'color_val']; + $obj = $normalizer->denormalize($data, SerializedNameDummy::class); + + $this->assertEquals('model_val', $obj->model); + $this->assertEquals('size_val', $obj->carSize); + $this->assertEquals('color_val', $obj->color); + } + + /** + * Test when the json data is named as the properties. They should be ignored. + */ + public function testSerializedNameDenormalizeWhenIgnoringSerializedName() + { + $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer->setSerializer($serializer); + + $data = ['model' => 'model_val', 'carSize' => 'size_val', 'color' => 'color_val']; + $obj = $normalizer->denormalize($data, SerializedNameDummy::class); + + $this->assertEquals(null, $obj->model); + $this->assertEquals(null, $obj->carSize); + $this->assertEquals('color_val', $obj->color); + } +} + +abstract class MetadataObjectSerializerNormalizer implements SerializerInterface, NormalizerInterface +{ +} + +class MicroSerializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface +{ + private $normalizer; + + public function __construct(NormalizerInterface $normalizer) + { + $this->normalizer = $normalizer; + } + + public function normalize($object, $format = null, array $context = array()) + { + return $this->normalizer->normalize($object, $format, $context); + } + + public function supportsNormalization($data, $format = null) + { + return $this->normalizer->normalize($data, $format); + } + + public function denormalize($data, $class, $format = null, array $context = array()) + { + if ($this->normalizer instanceof DenormalizerInterface) { + return $this->normalizer->denormalize($data, $class, $format, $context); + } + + throw new \RuntimeException('Test function not implemented'); + } + + public function supportsDenormalization($data, $type, $format = null) + { + if ($this->normalizer instanceof DenormalizerInterface) { + return $this->normalizer->supportsDenormalization($data, $type, $format); + } + + throw new \RuntimeException('Test function not implemented'); + } + + public function serialize($data, $format, array $context = array()) + { + throw new \RuntimeException('Test function not implemented'); + } + + public function deserialize($data, $type, $format, array $context = array()) + { + throw new \RuntimeException('Test function not implemented'); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/autoload.php b/src/Symfony/Component/Serializer/Tests/autoload.php new file mode 100644 index 0000000000000..70526bb5e4aa1 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/autoload.php @@ -0,0 +1,13 @@ + From d7ff93b2f0caa0e4d37c66e3e538d061f84a7d7e Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sun, 17 Jul 2016 20:17:46 +0200 Subject: [PATCH 02/13] Reverted test fix --- src/Symfony/Component/Serializer/Tests/autoload.php | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/Symfony/Component/Serializer/Tests/autoload.php diff --git a/src/Symfony/Component/Serializer/Tests/autoload.php b/src/Symfony/Component/Serializer/Tests/autoload.php deleted file mode 100644 index 70526bb5e4aa1..0000000000000 --- a/src/Symfony/Component/Serializer/Tests/autoload.php +++ /dev/null @@ -1,13 +0,0 @@ - Date: Sun, 17 Jul 2016 20:42:39 +0200 Subject: [PATCH 03/13] Removed code not needed --- .../Normalizer/AbstractObjectNormalizer.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index c64ccbb7d0127..7cb8e9cf1221a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -272,19 +272,6 @@ public function denormalize($data, $class, $format = null, array $context = arra return $object; } - /** - * {@inheritdoc} - */ - protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array()) - { - if (preg_match('/^(get|is|has|set)(.+)$/i', $attribute, $matches)) { - // We do not allow access to accessors or mutators directly - return false; - } - - return !in_array($attribute, $this->ignoredAttributes); - } - /** * Sets attribute value. * From a6838871eab8525b5b7f67a2fd1da86a3ddf2633 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sun, 17 Jul 2016 20:42:46 +0200 Subject: [PATCH 04/13] Make constructor parameter optional --- .../Component/Serializer/Normalizer/MetadataAwareNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index 691f7e2578f9b..67f503c364995 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -34,7 +34,7 @@ class MetadataAwareNormalizer extends AbstractObjectNormalizer protected $propertyAccessor; public function __construct( - ClassMetadataFactoryInterface $classMetadataFactory, + ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null) From 4a6b08f01eb59ec8be0e12b114d0ab66c1d3bd3e Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sun, 17 Jul 2016 20:47:01 +0200 Subject: [PATCH 05/13] Code style from fabpot.io --- .../Component/Serializer/Annotation/Accessor.php | 2 +- .../Component/Serializer/Annotation/ReadOnly.php | 1 + .../Serializer/Mapping/AttributeMetadata.php | 12 ++++++------ .../Mapping/AttributeMetadataInterface.php | 6 +++--- .../Component/Serializer/Mapping/ClassMetadata.php | 4 ++-- .../Serializer/Mapping/ClassMetadataInterface.php | 2 +- .../Serializer/Mapping/Loader/AnnotationLoader.php | 2 +- .../Mapping/Loader/BetterAnnotationLoader.php | 2 -- .../Serializer/Normalizer/AbstractNormalizer.php | 2 +- .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../Normalizer/MetadataAwareNormalizer.php | 6 ++---- .../MetadataAwarePropertyTypeExtractor.php | 14 +++++--------- .../PropertyManager/ReflectionPropertyAccess.php | 4 ++-- .../Serializer/Tests/Fixtures/CompositionDummy.php | 1 - 14 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/Symfony/Component/Serializer/Annotation/Accessor.php b/src/Symfony/Component/Serializer/Annotation/Accessor.php index 330d681df8e42..56d29982eddca 100644 --- a/src/Symfony/Component/Serializer/Annotation/Accessor.php +++ b/src/Symfony/Component/Serializer/Annotation/Accessor.php @@ -29,7 +29,7 @@ public function __construct(array $data) if (empty($data)) { throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); } - + foreach (array('getter', 'setter') as $parameter) { if (!isset($data[$parameter]) || !$data[$parameter]) { continue; diff --git a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php index f4065f65ea37c..fcb812d3c6a3d 100644 --- a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php +++ b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php @@ -24,6 +24,7 @@ public function __construct(array $data = array()) { if (empty($data) || !isset($data['value'])) { $this->readOnly = true; + return; } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index 4d1f56c29c710..78b6588636a60 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -158,7 +158,7 @@ public function setAccessorSetter($accessorSetter) } /** - * @return boolean + * @return bool */ public function getExclude() { @@ -166,7 +166,7 @@ public function getExclude() } /** - * @param boolean $exclude + * @param bool $exclude * * @return AttributeMetadata */ @@ -178,7 +178,7 @@ public function setExclude($exclude) } /** - * @return boolean + * @return bool */ public function getExpose() { @@ -186,7 +186,7 @@ public function getExpose() } /** - * @param boolean $expose + * @param bool $expose * * @return AttributeMetadata */ @@ -232,7 +232,7 @@ public function getMaxDepth() } /** - * @return boolean + * @return bool */ public function getReadOnly() { @@ -240,7 +240,7 @@ public function getReadOnly() } /** - * @param boolean $readOnly + * @param bool $readOnly * * @return AttributeMetadata */ diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index e0b0c3943b3c4..8bac9c0f9dade 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -61,7 +61,7 @@ public function setAccessorSetter($function); /** * True if this attribute should be excluded. * - * @return boolean|null + * @return bool|null */ public function getExclude(); @@ -75,7 +75,7 @@ public function setExclude($bool); /** * True if this attribute should be exposed. * - * @return boolean|null + * @return bool|null */ public function getExpose(); @@ -117,7 +117,7 @@ public function getMaxDepth(); /** * True if this attribute should be ignored when deserializing. * - * @return boolean|null + * @return bool|null */ public function getReadOnly(); diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php index e71ea55377213..4bd3d4ccb8c68 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php @@ -107,7 +107,7 @@ public function setExclusionPolicy($exclusionPolicy) } /** - * @return boolean + * @return bool */ public function getReadOnly() { @@ -115,7 +115,7 @@ public function getReadOnly() } /** - * @param boolean $readOnly + * @param bool $readOnly * * @return ClassMetadata */ diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php index b1627f63331d7..ac0eddea5ffad 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php @@ -49,7 +49,7 @@ public function setExclusionPolicy($policy); /** * True if this class should be ignored when deserializing. * - * @return boolean|null + * @return bool|null */ public function getReadOnly(); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index 823b1c0533cd1..0c195f671dad9 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -115,4 +115,4 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) return $loaded; } -} \ No newline at end of file +} diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php index 52a988f9acad9..cf47afd2cd4c5 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php @@ -13,9 +13,7 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Component\Serializer\Annotation; -use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\AttributeMetadata; -use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 2a4bfd713b229..5e026c4a6b6bd 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -288,7 +288,7 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null * the denormalization process. * * @param object|array $data - * @param string $class + * @param string $class * * @return array */ diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 7cb8e9cf1221a..c331123f8bb0f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -344,7 +344,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because the value is "%s", expected array', $attribute, $class, gettype($data))); } - $denormalizedData = []; + $denormalizedData = array(); $childClass = $type->getCollectionValueType()->getClassName(); foreach ($data as $d) { if ($this->serializer->supportsDenormalization($d, $childClass, $format)) { diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index 67f503c364995..a75d838ee829f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -11,11 +11,9 @@ namespace Symfony\Component\Serializer\Normalizer; -use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Annotation\ExclusionPolicy; -use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -216,14 +214,14 @@ protected function updateData(array $data, $attribute, $attributeValue, $object) */ protected function prepareForDenormalization($data, $class) { - $preparedData = []; + $preparedData = array(); $data = (array) $data; /** @var ClassMetadataInterface $classMetadata */ $classMetadata = $this->classMetadataFactory->getMetadataFor($class); $attributeMetadata = $classMetadata->getAttributesMetadata(); $classReadOnly = true === $classMetadata->getReadOnly(); - $validSerializedKeys = []; + $validSerializedKeys = array(); foreach ($attributeMetadata as $attributeName => $metadata) { $attributeReadOnly = $metadata->getReadOnly(); if ($attributeReadOnly === true || ($classReadOnly && $attributeReadOnly !== false)) { diff --git a/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php index f13fc7411342d..ea7f9b57a3b9e 100644 --- a/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php +++ b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php @@ -2,15 +2,12 @@ namespace Symfony\Component\Serializer\PropertyManager; -use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; /** - * - * * @author Tobias Nyholm */ class MetadataAwarePropertyTypeExtractor implements PropertyTypeExtractorInterface @@ -27,8 +24,8 @@ class MetadataAwarePropertyTypeExtractor implements PropertyTypeExtractorInterfa public function __construct(ClassMetadataFactoryInterface $classMetadataFactory, PropertyTypeExtractorInterface $propertyTypeExtractor = null) { - $this->classMetadataFactory = $classMetadataFactory; - $this->propertyTypeExtractor = $propertyTypeExtractor; + $this->classMetadataFactory = $classMetadataFactory; + $this->propertyTypeExtractor = $propertyTypeExtractor; } public function getTypes($class, $property, array $context = array()) @@ -41,16 +38,15 @@ public function getTypes($class, $property, array $context = array()) return array($this->convertStringToType($string)); } - if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class, $property)) { - return null; + return; } return $types; } /** - * Convert a plain string to a Type + * Convert a plain string to a Type. * * @param string $doctype */ @@ -93,4 +89,4 @@ private function getPhpTypeAndClass($docType) return array('object', substr($docType, 1)); } -} \ No newline at end of file +} diff --git a/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php b/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php index 00bb09ff502a2..2dcfb3cf71b78 100644 --- a/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php +++ b/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php @@ -14,7 +14,7 @@ class ReflectionPropertyAccess /** * @param object $object * @param string $propertyName - * @param mixed $value + * @param mixed $value */ public function setValue($object, $propertyName, $value) { @@ -36,7 +36,7 @@ public function getValue($object, $propertyName) } /** - * @param mixed $objectOrClass + * @param mixed $objectOrClass * @param string $propertyName * * @return \ReflectionProperty diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php index b9cbf7873a2c3..8c7d9a176d633 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php @@ -16,7 +16,6 @@ class CompositionDummy */ private $child; - public function __construct($withValues = false) { if ($withValues) { From d2929154eb4f7e864b9e8ce5a864da58cc297620 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 13:25:23 +0200 Subject: [PATCH 06/13] Renamed setters=>mutator, getters=>accessors --- .../Annotation/{Accessor.php => Methods.php} | 23 +++++-------- .../Serializer/Mapping/AttributeMetadata.php | 34 +++++++++---------- .../Mapping/AttributeMetadataInterface.php | 8 ++--- .../Mapping/Loader/BetterAnnotationLoader.php | 8 ++--- .../Normalizer/MetadataAwareNormalizer.php | 4 +-- .../Tests/Annotation/AccessorTest.php | 16 ++++----- .../Tests/Mapping/AttributeMetadataTest.php | 32 ++++++++--------- 7 files changed, 60 insertions(+), 65 deletions(-) rename src/Symfony/Component/Serializer/Annotation/{Accessor.php => Methods.php} (78%) diff --git a/src/Symfony/Component/Serializer/Annotation/Accessor.php b/src/Symfony/Component/Serializer/Annotation/Methods.php similarity index 78% rename from src/Symfony/Component/Serializer/Annotation/Accessor.php rename to src/Symfony/Component/Serializer/Annotation/Methods.php index 56d29982eddca..1c057db73cc86 100644 --- a/src/Symfony/Component/Serializer/Annotation/Accessor.php +++ b/src/Symfony/Component/Serializer/Annotation/Methods.php @@ -10,16 +10,17 @@ * * @author Tobias Nyholm */ -final class Accessor +final class Methods { /** * @var string */ - private $getter; + private $accessor; + /** * @var string */ - private $setter; + private $mutator; /** * @param array $data @@ -42,24 +43,18 @@ public function __construct(array $data) $this->$parameter = $data[$parameter]; } - if (null === $this->getter && null === $this->setter) { + if (null === $this->accessor && null === $this->mutator) { throw new InvalidArgumentException(sprintf('Either option "getter" or "setter" must be given for annotation %s', get_class($this))); } } - /** - * @return string - */ - public function getGetter() + public function getAccessor(): ?string { - return $this->getter; + return $this->accessor; } - /** - * @return string - */ - public function getSetter() + public function getMutator(): ?string { - return $this->setter; + return $this->mutator; } } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index 78b6588636a60..c7d32570d2536 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -32,7 +32,7 @@ class AttributeMetadata implements AttributeMetadataInterface * class' serialized representation. Do not access it. Use * {@link getGroups()} instead. */ - public $accessorGetter; + public $methodsAccessor; /** * @var string @@ -41,7 +41,7 @@ class AttributeMetadata implements AttributeMetadataInterface * class' serialized representation. Do not access it. Use * {@link getGroups()} instead. */ - public $accessorSetter; + public $methodsMutator; /** * @var bool @@ -120,19 +120,19 @@ public function getName() /** * @return string */ - public function getAccessorGetter() + public function getMethodsAccessor() { - return $this->accessorGetter; + return $this->methodsAccessor; } /** - * @param string $accessorGetter + * @param string $methodsAccessor * * @return AttributeMetadata */ - public function setAccessorGetter($accessorGetter) + public function setMethodsAccessor($methodsAccessor) { - $this->accessorGetter = $accessorGetter; + $this->methodsAccessor = $methodsAccessor; return $this; } @@ -140,19 +140,19 @@ public function setAccessorGetter($accessorGetter) /** * @return string */ - public function getAccessorSetter() + public function getMethodsMutator() { - return $this->accessorSetter; + return $this->methodsMutator; } /** - * @param string $accessorSetter + * @param string $mutator * * @return AttributeMetadata */ - public function setAccessorSetter($accessorSetter) + public function setMethodsMutator($mutator) { - $this->accessorSetter = $accessorSetter; + $this->methodsMutator = $mutator; return $this; } @@ -294,18 +294,18 @@ public function setType($type) /** * {@inheritdoc} */ - public function merge(AttributeMetadataInterface $attributeMetadata) + public function merge(self $attributeMetadata) { foreach ($attributeMetadata->getGroups() as $group) { $this->addGroup($group); } // Overwrite only if not defined - if (null === $this->accessorGetter) { - $this->accessorGetter = $attributeMetadata->getAccessorGetter(); + if (null === $this->methodsAccessor) { + $this->methodsAccessor = $attributeMetadata->getMethodsAccessor(); } - if (null === $this->accessorSetter) { - $this->accessorSetter = $attributeMetadata->getAccessorSetter(); + if (null === $this->methodsMutator) { + $this->methodsMutator = $attributeMetadata->getMethodsAccessor(); } if (null === $this->exclude) { $this->exclude = $attributeMetadata->getExclude(); diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index 8bac9c0f9dade..f423128125b28 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -35,28 +35,28 @@ public function getName(); * * @return string|null */ - public function getAccessorGetter(); + public function getMethodsAccessor(); /** * Set the getter for this attribute. * * @param string|null */ - public function setAccessorGetter($function); + public function setMethodsAccessor($function); /** * Get the setter for this attribute. * * @return string|null */ - public function getAccessorSetter(); + public function getMethodsMutator(); /** * Set the setter for this attribute. * * @param string|null */ - public function setAccessorSetter($function); + public function setMethodsMutator($function); /** * True if this attribute should be excluded. diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php index cf47afd2cd4c5..cb6b10164d439 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php @@ -60,9 +60,9 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) foreach ($annotation->getGroups() as $group) { $attributesMetadata[$property->name]->addGroup($group); } - } elseif ($annotation instanceof Annotation\Accessor) { - $attributesMetadata[$property->name]->setAccessorGetter($annotation->getGetter()); - $attributesMetadata[$property->name]->setAccessorSetter($annotation->getSetter()); + } elseif ($annotation instanceof Annotation\Methods) { + $attributesMetadata[$property->name]->setMethodsAccessor($annotation->getAccessor()); + $attributesMetadata[$property->name]->setMethodsMutator($annotation->getMutator()); } elseif ($annotation instanceof Annotation\Exclude) { $attributesMetadata[$property->name]->setExclude($annotation->getValue()); } elseif ($annotation instanceof Annotation\Expose) { @@ -99,7 +99,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $classMetadata->addAttributeMetadata($attributeMetadata); // Add default values for methods - $attributeMetadata->setAccessorGetter($methodName); + $attributeMetadata->setMethodsAccessor($methodName); $attributeMetadata->setExclude(true); $attributeMetadata->setReadOnly(true); diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index a75d838ee829f..b110cf9d2090c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -89,7 +89,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array $classMetadata = $this->classMetadataFactory->getMetadataFor($object); $attributeMetadata = $classMetadata->getAttributesMetadata(); - if (null !== $function = $attributeMetadata[$attribute]->getAccessorGetter()) { + if (null !== $function = $attributeMetadata[$attribute]->getMethodsAccessor()) { return $object->$function(); } @@ -105,7 +105,7 @@ protected function setAttributeValue($object, $attribute, $value, $format = null $classMetadata = $this->classMetadataFactory->getMetadataFor($object); $attributeMetadata = $classMetadata->getAttributesMetadata(); - if (null !== $function = $attributeMetadata[$attribute]->getAccessorSetter()) { + if (null !== $function = $attributeMetadata[$attribute]->getMethodsMutator()) { return $object->$function($value); } diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php index cbe83eb303583..4a88612ce0add 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Serializer\Tests\Annotation; -use Symfony\Component\Serializer\Annotation\Accessor; +use Symfony\Component\Serializer\Annotation\Methods; /** * @author Tobias Nyholm @@ -23,7 +23,7 @@ class AccessorTest extends \PHPUnit_Framework_TestCase */ public function testNoParameter() { - new Accessor(array()); + new Methods(array()); } /** @@ -31,7 +31,7 @@ public function testNoParameter() */ public function testInvalidParameter() { - new Accessor(array('value' => 'Foobar')); + new Methods(array('value' => 'Foobar')); } /** @@ -39,7 +39,7 @@ public function testInvalidParameter() */ public function testInvalidSetterParameter() { - new Accessor(array('setter' => new \stdClass())); + new Methods(array('setter' => new \stdClass())); } /** @@ -47,13 +47,13 @@ public function testInvalidSetterParameter() */ public function testInvalidGetterParameter() { - new Accessor(array('getter' => array())); + new Methods(array('getter' => array())); } public function testAccessorParameters() { - $accessor = new Accessor(array('setter' => 'Foo', 'getter' => 'Bar')); - $this->assertEquals('Bar', $accessor->getGetter()); - $this->assertEquals('Foo', $accessor->getSetter()); + $accessor = new Methods(array('setter' => 'Foo', 'getter' => 'Bar')); + $this->assertEquals('Bar', $accessor->getAccessor()); + $this->assertEquals('Foo', $accessor->getMutator()); } } diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php index 06ab1d203a1f1..701ad5f5c9fdc 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php @@ -35,14 +35,14 @@ public function testGetName() public function testAccessor() { $attributeMetadata = new AttributeMetadata('name'); - $this->assertNull($attributeMetadata->getAccessorGetter()); - $this->assertNull($attributeMetadata->getAccessorSetter()); + $this->assertNull($attributeMetadata->getMethodsAccessor()); + $this->assertNull($attributeMetadata->getMethodsMutator()); - $attributeMetadata->setAccessorGetter('getter'); - $this->assertEquals('getter', $attributeMetadata->getAccessorGetter()); + $attributeMetadata->setMethodsAccessor('getter'); + $this->assertEquals('getter', $attributeMetadata->getMethodsAccessor()); - $attributeMetadata->setAccessorSetter('setter'); - $this->assertEquals('setter', $attributeMetadata->getAccessorSetter()); + $attributeMetadata->setMethodsMutator('setter'); + $this->assertEquals('setter', $attributeMetadata->getMethodsMutator()); } public function testExclude() @@ -128,8 +128,8 @@ public function testMerge() $attributeMetadata2 = new AttributeMetadata('a2'); $attributeMetadata2->addGroup('a'); $attributeMetadata2->addGroup('c'); - $attributeMetadata2->setAccessorGetter('getter'); - $attributeMetadata2->setAccessorSetter('setter'); + $attributeMetadata2->setMethodsAccessor('getter'); + $attributeMetadata2->setMethodsMutator('setter'); $attributeMetadata2->setExclude(true); $attributeMetadata2->setExpose(false); $attributeMetadata2->setMaxDepth(2); @@ -140,8 +140,8 @@ public function testMerge() $attributeMetadata1->merge($attributeMetadata2); $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); - $this->assertEquals('getter', $attributeMetadata1->getAccessorGetter()); - $this->assertEquals('setter', $attributeMetadata1->getAccessorSetter()); + $this->assertEquals('getter', $attributeMetadata1->getMethodsAccessor()); + $this->assertEquals('setter', $attributeMetadata1->getMethodsMutator()); $this->assertEquals(true, $attributeMetadata1->getExclude()); $this->assertEquals(false, $attributeMetadata1->getExpose()); $this->assertEquals(2, $attributeMetadata1->getMaxDepth()); @@ -158,8 +158,8 @@ public function testMergeNoOverwrite() $attributeMetadata1 = new AttributeMetadata('a1'); $attributeMetadata1->addGroup('a'); $attributeMetadata1->addGroup('b'); - $attributeMetadata1->setAccessorGetter('getter'); - $attributeMetadata1->setAccessorSetter('setter'); + $attributeMetadata1->setMethodsAccessor('getter'); + $attributeMetadata1->setMethodsMutator('setter'); $attributeMetadata1->setExclude(true); $attributeMetadata1->setExpose(false); $attributeMetadata1->setMaxDepth(2); @@ -170,8 +170,8 @@ public function testMergeNoOverwrite() $attributeMetadata2 = new AttributeMetadata('a2'); $attributeMetadata2->addGroup('a'); $attributeMetadata2->addGroup('c'); - $attributeMetadata2->setAccessorGetter('getter2'); - $attributeMetadata2->setAccessorSetter('setter2'); + $attributeMetadata2->setMethodsAccessor('getter2'); + $attributeMetadata2->setMethodsMutator('setter2'); $attributeMetadata2->setExclude(false); $attributeMetadata2->setExpose(true); $attributeMetadata2->setMaxDepth(3); @@ -182,8 +182,8 @@ public function testMergeNoOverwrite() $attributeMetadata1->merge($attributeMetadata2); $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); - $this->assertEquals('getter', $attributeMetadata1->getAccessorGetter()); - $this->assertEquals('setter', $attributeMetadata1->getAccessorSetter()); + $this->assertEquals('getter', $attributeMetadata1->getMethodsAccessor()); + $this->assertEquals('setter', $attributeMetadata1->getMethodsMutator()); $this->assertEquals(true, $attributeMetadata1->getExclude()); $this->assertEquals(false, $attributeMetadata1->getExpose()); $this->assertEquals(2, $attributeMetadata1->getMaxDepth()); From aaada1cbe897bffde4643d80fcb08fcdd9050348 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 13:26:11 +0200 Subject: [PATCH 07/13] minor --- src/Symfony/Component/Serializer/Annotation/Methods.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Annotation/Methods.php b/src/Symfony/Component/Serializer/Annotation/Methods.php index 1c057db73cc86..2a59727ebd189 100644 --- a/src/Symfony/Component/Serializer/Annotation/Methods.php +++ b/src/Symfony/Component/Serializer/Annotation/Methods.php @@ -31,7 +31,7 @@ public function __construct(array $data) throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); } - foreach (array('getter', 'setter') as $parameter) { + foreach (array('accessor', 'mutator') as $parameter) { if (!isset($data[$parameter]) || !$data[$parameter]) { continue; } From bb09437a9622518e88a5efc1a7c01f4142ea2aa3 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 13:30:43 +0200 Subject: [PATCH 08/13] Removed some doc blocks --- .../Component/Serializer/Annotation/Exclude.php | 15 +-------------- .../Serializer/Annotation/ExclusionPolicy.php | 8 +------- .../Component/Serializer/Annotation/Expose.php | 15 +-------------- .../Component/Serializer/Annotation/MaxDepth.php | 6 ------ .../Component/Serializer/Annotation/Methods.php | 7 ++----- .../Component/Serializer/Annotation/ReadOnly.php | 8 +------- .../Serializer/Annotation/SerializedName.php | 5 +---- .../Component/Serializer/Annotation/Type.php | 8 +------- 8 files changed, 8 insertions(+), 64 deletions(-) diff --git a/src/Symfony/Component/Serializer/Annotation/Exclude.php b/src/Symfony/Component/Serializer/Annotation/Exclude.php index 0dce14c8923c3..e6a9558875cf1 100644 --- a/src/Symfony/Component/Serializer/Annotation/Exclude.php +++ b/src/Symfony/Component/Serializer/Annotation/Exclude.php @@ -12,20 +12,7 @@ */ final class Exclude { - /** - * @param array $data - */ - public function __construct(array $data = array()) - { - if (!empty($data)) { - throw new InvalidArgumentException(sprintf('No parameter is allowed for annotation "%s".', get_class($this))); - } - } - - /** - * @return bool - */ - public function getValue() + public function getValue(): bool { return true; } diff --git a/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php index f147638008b61..9fa43c51860d5 100644 --- a/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php +++ b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php @@ -20,9 +20,6 @@ final class ExclusionPolicy */ private $policy; - /** - * @param array $data - */ public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { @@ -40,10 +37,7 @@ public function __construct(array $data) } } - /** - * @return string - */ - public function getPolicy() + public function getPolicy(): string { return $this->policy; } diff --git a/src/Symfony/Component/Serializer/Annotation/Expose.php b/src/Symfony/Component/Serializer/Annotation/Expose.php index 8c82111bc0d2d..d766d08272f62 100644 --- a/src/Symfony/Component/Serializer/Annotation/Expose.php +++ b/src/Symfony/Component/Serializer/Annotation/Expose.php @@ -12,20 +12,7 @@ */ final class Expose { - /** - * @param array $data - */ - public function __construct(array $data = array()) - { - if (!empty($data)) { - throw new InvalidArgumentException(sprintf('No parameter is allowed for annotation "%s".', get_class($this))); - } - } - - /** - * @return bool - */ - public function getValue() + public function getValue(): bool { return true; } diff --git a/src/Symfony/Component/Serializer/Annotation/MaxDepth.php b/src/Symfony/Component/Serializer/Annotation/MaxDepth.php index 742b7acb28cc9..a274c20d85283 100644 --- a/src/Symfony/Component/Serializer/Annotation/MaxDepth.php +++ b/src/Symfony/Component/Serializer/Annotation/MaxDepth.php @@ -28,9 +28,6 @@ class MaxDepth */ private $maxDepth; - /** - * @param array $data - */ public function __construct(array $data) { if (!isset($data['value'])) { @@ -44,9 +41,6 @@ public function __construct(array $data) $this->maxDepth = $data['value']; } - /** - * @return int - */ public function getMaxDepth() { return $this->maxDepth; diff --git a/src/Symfony/Component/Serializer/Annotation/Methods.php b/src/Symfony/Component/Serializer/Annotation/Methods.php index 2a59727ebd189..a87bf933275f7 100644 --- a/src/Symfony/Component/Serializer/Annotation/Methods.php +++ b/src/Symfony/Component/Serializer/Annotation/Methods.php @@ -13,18 +13,15 @@ final class Methods { /** - * @var string + * @var null|string */ private $accessor; /** - * @var string + * @var null|string */ private $mutator; - /** - * @param array $data - */ public function __construct(array $data) { if (empty($data)) { diff --git a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php index fcb812d3c6a3d..a9f92ff47312c 100644 --- a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php +++ b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php @@ -17,9 +17,6 @@ final class ReadOnly */ private $readOnly; - /** - * @param array $data - */ public function __construct(array $data = array()) { if (empty($data) || !isset($data['value'])) { @@ -35,10 +32,7 @@ public function __construct(array $data = array()) $this->readOnly = $data['value']; } - /** - * @return bool - */ - public function getReadOnly() + public function getReadOnly(): bool { return $this->readOnly; } diff --git a/src/Symfony/Component/Serializer/Annotation/SerializedName.php b/src/Symfony/Component/Serializer/Annotation/SerializedName.php index e18a9c2fbdcc4..f373b8ed9a460 100644 --- a/src/Symfony/Component/Serializer/Annotation/SerializedName.php +++ b/src/Symfony/Component/Serializer/Annotation/SerializedName.php @@ -17,9 +17,6 @@ final class SerializedName */ private $name; - /** - * @param array $data - */ public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { @@ -33,7 +30,7 @@ public function __construct(array $data) $this->name = $data['value']; } - public function getName() + public function getName(): string { return $this->name; } diff --git a/src/Symfony/Component/Serializer/Annotation/Type.php b/src/Symfony/Component/Serializer/Annotation/Type.php index 4d04af0f47906..363113dd05197 100644 --- a/src/Symfony/Component/Serializer/Annotation/Type.php +++ b/src/Symfony/Component/Serializer/Annotation/Type.php @@ -17,9 +17,6 @@ final class Type */ private $type; - /** - * @param array $data - */ public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { @@ -42,10 +39,7 @@ public function __construct(array $data) $this->type = $data['value']; } - /** - * @return string - */ - public function getType() + public function getType(): string { return $this->type; } From 0fe9da5e8c4db0f2b455052297b6aa0843833e78 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 13:40:20 +0200 Subject: [PATCH 09/13] Updates according to feedback and style changes --- .../Serializer/Annotation/Exclude.php | 11 ++- .../Serializer/Annotation/ExclusionPolicy.php | 15 +++- .../Serializer/Annotation/Expose.php | 11 ++- .../Serializer/Annotation/Methods.php | 17 +++- .../Serializer/Annotation/ReadOnly.php | 13 +++- .../Serializer/Annotation/SerializedName.php | 15 +++- .../Component/Serializer/Annotation/Type.php | 19 +++-- .../Serializer/Mapping/AttributeMetadata.php | 14 ++-- .../Serializer/Mapping/ClassMetadata.php | 4 +- .../Mapping/Loader/BetterAnnotationLoader.php | 3 - .../Normalizer/AbstractNormalizer.php | 3 +- .../Normalizer/AbstractObjectNormalizer.php | 5 +- .../Normalizer/MetadataAwareNormalizer.php | 78 +++++++++---------- .../MetadataAwarePropertyTypeExtractor.php | 11 ++- .../ReflectionPropertyAccess.php | 71 ----------------- .../Tests/Annotation/AccessorTest.php | 2 +- .../Tests/Annotation/ExcludeTest.php | 2 +- .../Tests/Annotation/ExclusionPolicyTest.php | 2 +- .../Tests/Annotation/ExposeTest.php | 2 +- .../Tests/Annotation/ReadOnlyTest.php | 2 +- .../Tests/Annotation/SerializedNameTest.php | 2 +- .../Serializer/Tests/Annotation/TypeTest.php | 2 +- .../Fixtures/ExclusionPolicyAllDummy.php | 1 + .../MetadataAwareNormalizerTest.php | 6 +- .../Component/Serializer/composer.json | 4 +- 25 files changed, 151 insertions(+), 164 deletions(-) delete mode 100644 src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php diff --git a/src/Symfony/Component/Serializer/Annotation/Exclude.php b/src/Symfony/Component/Serializer/Annotation/Exclude.php index e6a9558875cf1..7af32a071c3da 100644 --- a/src/Symfony/Component/Serializer/Annotation/Exclude.php +++ b/src/Symfony/Component/Serializer/Annotation/Exclude.php @@ -1,8 +1,15 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ -use Symfony\Component\Serializer\Exception\InvalidArgumentException; +namespace Symfony\Component\Serializer\Annotation; /** * @Annotation diff --git a/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php index 9fa43c51860d5..0982fd8032a22 100644 --- a/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php +++ b/src/Symfony/Component/Serializer/Annotation/ExclusionPolicy.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\Annotation; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -23,11 +32,11 @@ final class ExclusionPolicy public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', \get_class($this))); } - if (!is_string($data['value'])) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + if (!\is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', \get_class($this))); } $this->policy = strtoupper($data['value']); diff --git a/src/Symfony/Component/Serializer/Annotation/Expose.php b/src/Symfony/Component/Serializer/Annotation/Expose.php index d766d08272f62..dc8911418d80d 100644 --- a/src/Symfony/Component/Serializer/Annotation/Expose.php +++ b/src/Symfony/Component/Serializer/Annotation/Expose.php @@ -1,8 +1,15 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ -use Symfony\Component\Serializer\Exception\InvalidArgumentException; +namespace Symfony\Component\Serializer\Annotation; /** * @Annotation diff --git a/src/Symfony/Component/Serializer/Annotation/Methods.php b/src/Symfony/Component/Serializer/Annotation/Methods.php index a87bf933275f7..d34bedfd7f89a 100644 --- a/src/Symfony/Component/Serializer/Annotation/Methods.php +++ b/src/Symfony/Component/Serializer/Annotation/Methods.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\Annotation; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -25,7 +34,7 @@ final class Methods public function __construct(array $data) { if (empty($data)) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', \get_class($this))); } foreach (array('accessor', 'mutator') as $parameter) { @@ -33,15 +42,15 @@ public function __construct(array $data) continue; } - if (!is_string($data[$parameter])) { - throw new InvalidArgumentException(sprintf('Parameter "%s" of annotation "%s" must be a string.', $parameter, get_class($this))); + if (!\is_string($data[$parameter])) { + throw new InvalidArgumentException(sprintf('Parameter "%s" of annotation "%s" must be a string.', $parameter, \get_class($this))); } $this->$parameter = $data[$parameter]; } if (null === $this->accessor && null === $this->mutator) { - throw new InvalidArgumentException(sprintf('Either option "getter" or "setter" must be given for annotation %s', get_class($this))); + throw new InvalidArgumentException(sprintf('Either option "getter" or "setter" must be given for annotation %s', \get_class($this))); } } diff --git a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php index a9f92ff47312c..598f3961aab13 100644 --- a/src/Symfony/Component/Serializer/Annotation/ReadOnly.php +++ b/src/Symfony/Component/Serializer/Annotation/ReadOnly.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\Annotation; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -25,8 +34,8 @@ public function __construct(array $data = array()) return; } - if (!is_bool($data['value'])) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a boolean.', get_class($this))); + if (!\is_bool($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a boolean.', \get_class($this))); } $this->readOnly = $data['value']; diff --git a/src/Symfony/Component/Serializer/Annotation/SerializedName.php b/src/Symfony/Component/Serializer/Annotation/SerializedName.php index f373b8ed9a460..609744179851b 100644 --- a/src/Symfony/Component/Serializer/Annotation/SerializedName.php +++ b/src/Symfony/Component/Serializer/Annotation/SerializedName.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\Annotation; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -20,11 +29,11 @@ final class SerializedName public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', \get_class($this))); } - if (!is_string($data['value'])) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + if (!\is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', \get_class($this))); } $this->name = $data['value']; diff --git a/src/Symfony/Component/Serializer/Annotation/Type.php b/src/Symfony/Component/Serializer/Annotation/Type.php index 363113dd05197..0be21fd5fd274 100644 --- a/src/Symfony/Component/Serializer/Annotation/Type.php +++ b/src/Symfony/Component/Serializer/Annotation/Type.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\Annotation; use Symfony\Component\Serializer\Exception\InvalidArgumentException; @@ -20,19 +29,19 @@ final class Type public function __construct(array $data) { if (!isset($data['value']) || !$data['value']) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', get_class($this))); + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" cannot be empty.', \get_class($this))); } - if (!is_string($data['value'])) { - throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', get_class($this))); + if (!\is_string($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a string.', \get_class($this))); } $type = $data['value']; if (false !== $pos = strpos($type, '\\')) { // This is a referencet to a class - if ($pos !== 0) { - throw new InvalidArgumentException(sprintf('When referring to an class you you must begin the type with backslash (\\) you provided "%s" for annotation "%s".', $type, get_class($this))); + if (0 !== $pos) { + throw new InvalidArgumentException(sprintf('When referring to an class you you must begin the type with backslash (\\) you provided "%s" for annotation "%s".', $type, \get_class($this))); } } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index c7d32570d2536..0c30b81ed97f7 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -30,7 +30,7 @@ class AttributeMetadata implements 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 getMethodsAccessor()} instead. */ public $methodsAccessor; @@ -39,7 +39,7 @@ class AttributeMetadata implements 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 getMethodsMutator()} instead. */ public $methodsMutator; @@ -48,7 +48,7 @@ class AttributeMetadata implements 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 getExclude()} instead. */ public $exclude; @@ -57,7 +57,7 @@ class AttributeMetadata implements 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 getExpose()} instead. */ public $expose; @@ -82,7 +82,7 @@ class AttributeMetadata implements 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 getReadOnly()} instead. */ public $readOnly; @@ -91,7 +91,7 @@ class AttributeMetadata implements 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 getSerializedName()} instead. */ public $serializedName; @@ -100,7 +100,7 @@ class AttributeMetadata implements 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 getType()} instead. */ public $type; diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php index 4bd3d4ccb8c68..36d01024adfa2 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php @@ -30,7 +30,7 @@ class ClassMetadata implements ClassMetadataInterface * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use - * {@link getName()} instead. + * {@link getExclusionPolicy()} instead. */ public $exclusionPolicy; @@ -39,7 +39,7 @@ class ClassMetadata implements ClassMetadataInterface * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use - * {@link getName()} instead. + * {@link getReadOnly()} instead. */ public $readOnly; diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php index cb6b10164d439..a312331e461c4 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/BetterAnnotationLoader.php @@ -29,9 +29,6 @@ class BetterAnnotationLoader implements LoaderInterface */ private $reader; - /** - * @param Reader $reader - */ public function __construct(Reader $reader) { $this->reader = $reader; diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 5e026c4a6b6bd..bc6f68b7e88db 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -288,11 +288,10 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null * the denormalization process. * * @param object|array $data - * @param string $class * * @return array */ - protected function prepareForDenormalization($data, $class) + protected function prepareForDenormalization($data) { return (array) $data; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index c331123f8bb0f..fe791c0ac8c58 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -340,8 +340,8 @@ private function validateAndDenormalize(string $currentClass, string $attribute, throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class)); } - if (!is_array($data) && !$data instanceof \Traversable) { - throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because the value is "%s", expected array', $attribute, $class, gettype($data))); + if (!\is_array($data) && !$data instanceof \Traversable) { + throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because the value is "%s", expected array', $attribute, $class, \gettype($data))); } $denormalizedData = array(); @@ -418,6 +418,7 @@ private function getTypes(string $currentClass, string $attribute) * Sets an attribute and apply the name converter if necessary. * * @param mixed $attributeValue + * * @internal */ protected function updateData(array $data, string $attribute, $attributeValue): array diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index b110cf9d2090c..cad1b0a25c5a2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -11,37 +11,36 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Annotation\ExclusionPolicy; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; -use Symfony\Component\Serializer\PropertyManager\ReflectionPropertyAccess; /** * Converts between objects and arrays using the Reflection and respect the metadata. * * @author Tobias Nyholm */ -class MetadataAwareNormalizer extends AbstractObjectNormalizer +final class MetadataAwareNormalizer extends AbstractObjectNormalizer { /** - * @var ReflectionPropertyAccess + * @var PropertyAccessorInterface */ protected $propertyAccessor; - public function __construct( - ClassMetadataFactoryInterface $classMetadataFactory = null, - NameConverterInterface $nameConverter = null, - PropertyAccessorInterface $propertyAccessor = null, - PropertyTypeExtractorInterface $propertyTypeExtractor = null) - { - parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor); - - $this->propertyAccessor = new ReflectionPropertyAccess(); + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, PropertyAccessorInterface $propertyAccessor = null) { + if (null === $propertyAccessor) { + $propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + $this->propertyAccessor = $propertyAccessor; + parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver); } + /** * {@inheritdoc} */ @@ -54,7 +53,7 @@ protected function extractAttributes($object, $format = null, array $context = a $reflClass = new \ReflectionClass($object); foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { if ( - $reflMethod->getNumberOfRequiredParameters() !== 0 || + 0 !== $reflMethod->getNumberOfRequiredParameters() || $reflMethod->isStatic() || $reflMethod->isConstructor() || $reflMethod->isDestructor() @@ -136,7 +135,7 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null return true; } - if ($classMetadata->getExclusionPolicy() === ExclusionPolicy::ALL) { + if (ExclusionPolicy::ALL === $classMetadata->getExclusionPolicy()) { return false; } @@ -149,7 +148,7 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null */ public function supportsNormalization($data, $format = null) { - if (!is_object($data)) { + if (!\is_object($data)) { return false; } @@ -161,37 +160,20 @@ public function supportsNormalization($data, $format = null) } /** + * Update data for normalization. + * * {@inheritdoc} */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) + protected function updateData(array $data, string $attribute, $attributeValue/*, $object*/): array { - if ( - isset($context[AbstractNormalizer::OBJECT_TO_POPULATE]) && - is_object($context[AbstractNormalizer::OBJECT_TO_POPULATE]) && - $context[AbstractNormalizer::OBJECT_TO_POPULATE] instanceof $class - ) { - $object = $context[AbstractNormalizer::OBJECT_TO_POPULATE]; - unset($context[AbstractNormalizer::OBJECT_TO_POPULATE]); - - return $object; - } + if (3 === \func_num_args()) { + @trigger_error('Fourth argument to MetadataAwareNormalizer must be the object.', E_USER_DEPRECATED); - $reflectionClass = new \ReflectionClass($class); - $constructor = $reflectionClass->getConstructor(); - if (!$constructor) { - return new $class(); + return $data; } + $object = func_get_arg(3); - return $reflectionClass->newInstanceWithoutConstructor(); - } - /** - * Update data for normalization. - * - * {@inheritdoc} - */ - protected function updateData(array $data, $attribute, $attributeValue, $object) - { /** @var ClassMetadataInterface $classMetadata */ $classMetadata = $this->classMetadataFactory->getMetadataFor($object); $attributeMetadata = $classMetadata->getAttributesMetadata(); @@ -212,19 +194,29 @@ protected function updateData(array $data, $attribute, $attributeValue, $object) * * {@inheritdoc} */ - protected function prepareForDenormalization($data, $class) + protected function prepareForDenormalization($data/*, string $class*/) { + if (1 === \func_num_args()) { + @trigger_error('Second argument to MetadataAwareNormalizer must be the class name', E_USER_DEPRECATED); + return (array) $data; + } + + $class = func_get_arg(1); $preparedData = array(); $data = (array) $data; /** @var ClassMetadataInterface $classMetadata */ $classMetadata = $this->classMetadataFactory->getMetadataFor($class); $attributeMetadata = $classMetadata->getAttributesMetadata(); - $classReadOnly = true === $classMetadata->getReadOnly(); + + // We should not do anything if the class is read only. + if (true === $classMetadata->getReadOnly()) { + return array(); + } $validSerializedKeys = array(); foreach ($attributeMetadata as $attributeName => $metadata) { $attributeReadOnly = $metadata->getReadOnly(); - if ($attributeReadOnly === true || ($classReadOnly && $attributeReadOnly !== false)) { + if (true === $attributeReadOnly || false !== $attributeReadOnly) { // This is not a valid key continue; } @@ -246,7 +238,7 @@ protected function prepareForDenormalization($data, $class) $validSerializedKeyNames = array_keys($validSerializedKeys); foreach ($data as $serializedKeyName => $value) { - if (in_array($serializedKeyName, $validSerializedKeyNames)) { + if (\in_array($serializedKeyName, $validSerializedKeyNames)) { // Replace with the keys for the serialized attribute $preparedData[$validSerializedKeys[$serializedKeyName]] = $value; } diff --git a/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php index ea7f9b57a3b9e..90d583608f098 100644 --- a/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php +++ b/src/Symfony/Component/Serializer/PropertyManager/MetadataAwarePropertyTypeExtractor.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\Serializer\PropertyManager; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; @@ -83,7 +92,7 @@ private function convertStringToType($docType) */ private function getPhpTypeAndClass($docType) { - if (in_array($docType, Type::$builtinTypes)) { + if (\in_array($docType, Type::$builtinTypes)) { return array($docType, null); } diff --git a/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php b/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php deleted file mode 100644 index 2dcfb3cf71b78..0000000000000 --- a/src/Symfony/Component/Serializer/PropertyManager/ReflectionPropertyAccess.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ -class ReflectionPropertyAccess -{ - /** - * @param object $object - * @param string $propertyName - * @param mixed $value - */ - public function setValue($object, $propertyName, $value) - { - $reflectionProperty = $this->getReflectionProperty($object, $propertyName); - $reflectionProperty->setValue($object, $value); - } - - /** - * @param object $object - * @param string $propertyName - * - * @return mixed - */ - public function getValue($object, $propertyName) - { - $reflectionProperty = $this->getReflectionProperty($object, $propertyName); - - return $reflectionProperty->getValue($object); - } - - /** - * @param mixed $objectOrClass - * @param string $propertyName - * - * @return \ReflectionProperty - */ - private function getReflectionProperty($objectOrClass, $propertyName) - { - $reflectionClass = new \ReflectionClass($objectOrClass); - - if (!$reflectionClass->hasProperty($propertyName)) { - if (false === $parent = get_parent_class($objectOrClass)) { - if (is_object($objectOrClass)) { - $objectOrClass = get_class($objectOrClass); - } - throw new LogicException(sprintf('There is no property "%s" on class "%s"', $propertyName, $objectOrClass)); - } - - try { - return $this->getReflectionProperty($parent, $propertyName); - } catch (LogicException $e) { - if (is_object($objectOrClass)) { - $objectOrClass = get_class($objectOrClass); - } - throw new LogicException(sprintf('There is no property "%s" on class "%s"', $propertyName, $objectOrClass), 0, $e); - } - } - - $reflectionProperty = $reflectionClass->getProperty($propertyName); - $reflectionProperty->setAccessible(true); - - return $reflectionProperty; - } -} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php index 4a88612ce0add..055b4d57a472b 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class AccessorTest extends \PHPUnit_Framework_TestCase +class AccessorTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php index a1817cf7f853d..5cac8e5d7eef9 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class ExcludeTest extends \PHPUnit_Framework_TestCase +class ExcludeTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php index 7c74b44fce863..ad91b0e67c5db 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExclusionPolicyTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class ExclusionPolicyTest extends \PHPUnit_Framework_TestCase +class ExclusionPolicyTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php index 5895620db1857..1ce0464b94e05 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class ExposeTest extends \PHPUnit_Framework_TestCase +class ExposeTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php index dc5c4be7b3f85..031cb17b5aa6a 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ReadOnlyTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class ReadOnlyTest extends \PHPUnit_Framework_TestCase +class ReadOnlyTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php index e41a9cb7acefc..68e0b09a8a1a6 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class SerializedNameTest extends \PHPUnit_Framework_TestCase +class SerializedNameTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php index 72c172531d9f8..03a7420e09f8d 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/TypeTest.php @@ -16,7 +16,7 @@ /** * @author Tobias Nyholm */ -class TypeTest extends \PHPUnit_Framework_TestCase +class TypeTest extends \PHPUnit\Framework\TestCase { /** * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php index c15005b241bad..c28be7808bcea 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ExclusionPolicyAllDummy.php @@ -6,6 +6,7 @@ /** * @Serializer\ExclusionPolicy("all") + * * @author Tobias Nyholm */ class ExclusionPolicyAllDummy diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php index 397a89ffd83f8..f4cd78a3fc145 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php @@ -53,7 +53,7 @@ protected function setUp() { $this->serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $this->normalizer = new MetadataAwareNormalizer($classMetadataFactory, null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $this->normalizer = new MetadataAwareNormalizer($classMetadataFactory, null, null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $this->normalizer->setSerializer($this->serializer); } @@ -305,7 +305,7 @@ public function testSerializedNameDenormalize() { $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $normalizer->setSerializer($serializer); $data = ['super_model' => 'model_val', 'car_size' => 'size_val', 'color' => 'color_val']; @@ -323,7 +323,7 @@ public function testSerializedNameDenormalizeWhenIgnoringSerializedName() { $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $normalizer->setSerializer($serializer); $data = ['model' => 'model_val', 'carSize' => 'size_val', 'color' => 'color_val']; diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 52961bb7c0528..4e1afa3388e9e 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" + "symfony/polyfill-ctype": "~1.8", + "symfony/property-access": "~3.4|~4.0" }, "require-dev": { "symfony/yaml": "~3.4|~4.0", "symfony/config": "~3.4|~4.0", - "symfony/property-access": "~3.4|~4.0", "symfony/http-foundation": "~3.4|~4.0", "symfony/cache": "~3.4|~4.0", "symfony/property-info": "~3.4|~4.0", From d28a7e29b14150ec3b3f97789f73159d068a6ec1 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 15:22:59 +0200 Subject: [PATCH 10/13] Added support for Yaml --- .../Mapping/Loader/YamlFileLoader.php | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index 10345afc0595d..1503d9bbb1583 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -52,7 +52,6 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } $yaml = $this->classes[$classMetadata->getName()]; - if (isset($yaml['attributes']) && \is_array($yaml['attributes'])) { $attributesMetadata = $classMetadata->getAttributesMetadata(); @@ -78,6 +77,44 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } } + if (isset($data['methods'])) { + if (!\is_array($data['methods'])) { + throw new MappingException(sprintf('The "methods" key must be an array in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + foreach ($data['methods'] as $methods) { + if (isset($methods['accessor'])) { + if (!\is_string($methods['accessor'])) { + throw new MappingException(sprintf('The value of "methods.accessor" must be a in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + $attributeMetadata->setMethodsAccessor($methods['accessor']); + } + + if (isset($methods['mutator'])) { + if (!\is_string($methods['mutator'])) { + throw new MappingException(sprintf('The value of "methods.mutator" must be a in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + $attributeMetadata->setMethodsAccessor($methods['mutator']); + } + } + } + + if (isset($data['exclude'])) { + if (!\is_bool($data['exclude'])) { + throw new MappingException(sprintf('The "exclude" value must be a boolean in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setExclude($data['exclude']); + } + + if (isset($data['expose'])) { + if (!\is_bool($data['expose'])) { + throw new MappingException(sprintf('The "expose" value must be a boolean in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setExpose($data['expose']); + } + if (isset($data['max_depth'])) { if (!\is_int($data['max_depth'])) { throw new MappingException(sprintf('The "max_depth" value must be an integer in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); @@ -85,7 +122,46 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $attributeMetadata->setMaxDepth($data['max_depth']); } + + if (isset($data['read_only'])) { + if (!\is_bool($data['read_only'])) { + throw new MappingException(sprintf('The "read_only" value must be a boolean in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setReadOnly($data['read_only']); + } + + if (isset($data['serialized_name'])) { + if (!\is_string($data['serialized_name'])) { + throw new MappingException(sprintf('The "serialized_name" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setReadOnly($data['serialized_name']); + } + + if (isset($data['type'])) { + if (!\is_string($data['type'])) { + throw new MappingException(sprintf('The "type" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setReadOnly($data['type']); + } + } + } + + if (isset($yaml['exclusion_policy'])) { + if (!\is_string($yaml['exclusion_policy'])) { + throw new MappingException(sprintf('The "exclusion_policy" value must be a string in "%s" for the class "%s".', $this->file, $classMetadata->getName())); } + + $classMetadata->setExclusionPolicy($yaml['exclusion_policy']); + } + if (isset($yaml['read_only'])) { + if (!\is_bool($yaml['read_only'])) { + throw new MappingException(sprintf('The "read_only" value must be a boolean in "%s" for the class "%s".', $this->file, $classMetadata->getName())); + } + + $classMetadata->setReadOnly($yaml['read_only']); } if (isset($yaml['discriminator_map'])) { From 0e96e86491e6ecdd2d9cf519ada2bad03a1351f0 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 15:29:44 +0200 Subject: [PATCH 11/13] Added support for XML --- .../Mapping/Loader/XmlFileLoader.php | 38 +++++++++++++++++++ .../Mapping/Loader/YamlFileLoader.php | 1 + .../Normalizer/MetadataAwareNormalizer.php | 8 ++-- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php index eec766f91d533..03ffb130b1ca2 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php @@ -63,9 +63,47 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $attributeMetadata->addGroup((string) $group); } + foreach ($attribute->methods as $methods) { + if (isset($methods['accessor'])) { + $attributeMetadata->setMethodsAccessor((string) $methods['accessor']); + } + + if (isset($methods['mutator'])) { + $attributeMetadata->setMethodsMutator((string) $methods['mutator']); + } + } + + if (isset($attribute['exclude'])) { + $attributeMetadata->setExclude((bool) $attribute['exclude']); + } + + if (isset($attribute['expose'])) { + $attributeMetadata->setExpose((bool) $attribute['expose']); + } + if (isset($attribute['max-depth'])) { $attributeMetadata->setMaxDepth((int) $attribute['max-depth']); } + + if (isset($attribute['read-only'])) { + $attributeMetadata->setReadOnly((bool) $attribute['read-only']); + } + + if (isset($attribute['serialized-name'])) { + $attributeMetadata->setSerializedName((string) $attribute['serialized-name']); + } + + if (isset($attribute['type'])) { + $attributeMetadata->setType((string) $attribute['type']); + } + } + + if (isset($xml['exclusion-policy'])) { + $classMetadata->setExclusionPolicy((string) $xml['exclusion-policy']); + } + + if (isset($xml['read-only'])) { + $classMetadata->setReadOnly((bool) $xml['read-only']); } if (isset($xml->{'discriminator-map'})) { diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index 1503d9bbb1583..f5af3d96d4338 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -156,6 +156,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $classMetadata->setExclusionPolicy($yaml['exclusion_policy']); } + if (isset($yaml['read_only'])) { if (!\is_bool($yaml['read_only'])) { throw new MappingException(sprintf('The "read_only" value must be a boolean in "%s" for the class "%s".', $this->file, $classMetadata->getName())); diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index cad1b0a25c5a2..23e90c9fba23c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -32,7 +32,8 @@ final class MetadataAwareNormalizer extends AbstractObjectNormalizer */ protected $propertyAccessor; - public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, PropertyAccessorInterface $propertyAccessor = null) { + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, PropertyAccessorInterface $propertyAccessor = null) + { if (null === $propertyAccessor) { $propertyAccessor = PropertyAccess::createPropertyAccessor(); } @@ -40,7 +41,6 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver); } - /** * {@inheritdoc} */ @@ -173,7 +173,6 @@ protected function updateData(array $data, string $attribute, $attributeValue/* } $object = func_get_arg(3); - /** @var ClassMetadataInterface $classMetadata */ $classMetadata = $this->classMetadataFactory->getMetadataFor($object); $attributeMetadata = $classMetadata->getAttributesMetadata(); @@ -198,10 +197,11 @@ protected function prepareForDenormalization($data/*, string $class*/) { if (1 === \func_num_args()) { @trigger_error('Second argument to MetadataAwareNormalizer must be the class name', E_USER_DEPRECATED); + return (array) $data; } - $class = func_get_arg(1); + $class = func_get_arg(1); $preparedData = array(); $data = (array) $data; /** @var ClassMetadataInterface $classMetadata */ From ec0474d28a2027a6d4e23887f7d60b7dbe5ca78d Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 1 Sep 2018 16:30:03 +0200 Subject: [PATCH 12/13] Updating tests --- .../Serializer/Mapping/AttributeMetadata.php | 8 ++++---- .../Normalizer/MetadataAwareNormalizer.php | 14 +++++++------ .../Tests/Annotation/AccessorTest.php | 2 +- .../Tests/Annotation/ExcludeTest.php | 8 -------- .../Tests/Annotation/ExposeTest.php | 8 -------- .../Tests/Fixtures/AccessorDummy.php | 2 +- .../Tests/Fixtures/CompositionDummy.php | 8 +++++--- .../Tests/Fixtures/ReadOnlyClassDummy.php | 6 ------ .../MetadataAwareNormalizerTest.php | 20 ++++++++++--------- 9 files changed, 30 insertions(+), 46 deletions(-) diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index 0c30b81ed97f7..5d9b544ab7d2b 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -294,7 +294,7 @@ public function setType($type) /** * {@inheritdoc} */ - public function merge(self $attributeMetadata) + public function merge(AttributeMetadataInterface $attributeMetadata) { foreach ($attributeMetadata->getGroups() as $group) { $this->addGroup($group); @@ -305,7 +305,7 @@ public function merge(self $attributeMetadata) $this->methodsAccessor = $attributeMetadata->getMethodsAccessor(); } if (null === $this->methodsMutator) { - $this->methodsMutator = $attributeMetadata->getMethodsAccessor(); + $this->methodsMutator = $attributeMetadata->getMethodsMutator(); } if (null === $this->exclude) { $this->exclude = $attributeMetadata->getExclude(); @@ -336,8 +336,8 @@ public function __sleep() { return array( 'name', - 'accessorGetter', - 'accessorSetter', + 'methodsAccessor', + 'methodsMutator', 'exclude', 'expose', 'groups', diff --git a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php index 23e90c9fba23c..d5cf285b153c3 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MetadataAwareNormalizer.php @@ -127,12 +127,14 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null $classMetadata = $this->classMetadataFactory->getMetadataFor($classOrObject); $attributeMetadata = $classMetadata->getAttributesMetadata(); - if (true === $attributeMetadata[$attribute]->getExclude()) { - return false; - } + if (isset($attributeMetadata[$attribute])) { + if (true === $attributeMetadata[$attribute]->getExclude()) { + return false; + } - if (true === $attributeMetadata[$attribute]->getExpose()) { - return true; + if (true === $attributeMetadata[$attribute]->getExpose()) { + return true; + } } if (ExclusionPolicy::ALL === $classMetadata->getExclusionPolicy()) { @@ -216,7 +218,7 @@ protected function prepareForDenormalization($data/*, string $class*/) $validSerializedKeys = array(); foreach ($attributeMetadata as $attributeName => $metadata) { $attributeReadOnly = $metadata->getReadOnly(); - if (true === $attributeReadOnly || false !== $attributeReadOnly) { + if (true === $attributeReadOnly) { // This is not a valid key continue; } diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php index 055b4d57a472b..5bf8866c94c49 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/AccessorTest.php @@ -52,7 +52,7 @@ public function testInvalidGetterParameter() public function testAccessorParameters() { - $accessor = new Methods(array('setter' => 'Foo', 'getter' => 'Bar')); + $accessor = new Methods(array('mutator' => 'Foo', 'accessor' => 'Bar')); $this->assertEquals('Bar', $accessor->getAccessor()); $this->assertEquals('Foo', $accessor->getMutator()); } diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php index 5cac8e5d7eef9..a7afe36e51c2b 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExcludeTest.php @@ -18,14 +18,6 @@ */ class ExcludeTest extends \PHPUnit\Framework\TestCase { - /** - * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException - */ - public function testInvalidParameter() - { - new Exclude(array('value' => 'Foobar')); - } - public function testExclude() { $exclude = new Exclude(); diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php index 1ce0464b94e05..de1f4a749cd4e 100644 --- a/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Annotation/ExposeTest.php @@ -18,14 +18,6 @@ */ class ExposeTest extends \PHPUnit\Framework\TestCase { - /** - * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException - */ - public function testInvalidParameter() - { - new Expose(array('value' => 'Foobar')); - } - public function testExpose() { $expose = new Expose(); diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php index afb0bc76cdc2f..704de7a123eb2 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AccessorDummy.php @@ -7,7 +7,7 @@ class AccessorDummy { /** - * @Serializer\Accessor(getter="getModel", setter="setModel") + * @Serializer\Methods(accessor="getModel", mutator="setModel") */ public $model = 'defaultValue'; diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php index 8c7d9a176d633..678035ce0be4a 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/CompositionDummy.php @@ -24,11 +24,13 @@ public function __construct($withValues = false) } } - /** - * @return Car - */ public function getChild() { return $this->child; } + + public function setChild(CompositionChildDummy $child) + { + $this->child = $child; + } } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php index eda46a67a5311..7b77beda1955a 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ReadOnlyClassDummy.php @@ -10,16 +10,10 @@ */ class ReadOnlyClassDummy { - /** - * @Serializer\ReadOnly(false) - */ public $model; public $size; - /** - * @Serializer\ReadOnly() - */ public $color; public function __construct($withValues = false) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php index f4cd78a3fc145..624a9fd4b0a75 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/MetadataAwareNormalizerTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use Doctrine\Common\Annotations\AnnotationReader; +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor; use Symfony\Component\Serializer\Mapping\Loader\BetterAnnotationLoader; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -38,7 +40,7 @@ /** * @author Tobias Nyholm */ -class MetadataAwareNormalizerTest extends \PHPUnit_Framework_TestCase +class MetadataAwareNormalizerTest extends TestCase { /** * @var MetadataAwareNormalizer @@ -51,9 +53,9 @@ class MetadataAwareNormalizerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $this->serializer = $this->getMockBuilder(__NAMESPACE__.'\MetadataObjectSerializerNormalizer')->getMock(); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $this->normalizer = new MetadataAwareNormalizer($classMetadataFactory, null, null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $this->normalizer = new MetadataAwareNormalizer($classMetadataFactory, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $this->normalizer->setSerializer($this->serializer); } @@ -91,7 +93,7 @@ public function testCompositionDenormalize() $serializer = new MicroSerializer($this->normalizer); $this->normalizer->setSerializer($serializer); - $data = json_decode('{"name":"Foobar","child":{"super_model":"val_model","car_size":"val_size","color":"val_color"}}', true); + $data = json_decode('{"name":"Foobar","child":{"super_model":"val_model","carSize":"val_size","color":"val_color"}}', true); $obj = $this->normalizer->denormalize($data, CompositionDummy::class); $this->assertEquals('Foobar', $obj->name); @@ -287,7 +289,7 @@ public function testReadOnlyDenormalizeClass() $data = ['model' => 'model_value', 'size' => 'size_value', 'color' => 'color_value']; $obj = $this->normalizer->denormalize($data, ReadOnlyClassDummy::class); - $this->assertEquals('model_value', $obj->model); + $this->assertEquals(null, $obj->model); $this->assertEquals(null, $obj->size); $this->assertEquals(null, $obj->color); } @@ -303,9 +305,9 @@ public function testSerializedNameNormalize() public function testSerializedNameDenormalize() { - $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $serializer = $this->getMockBuilder(__NAMESPACE__.'\MetadataObjectSerializerNormalizer')->getMock(); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $normalizer->setSerializer($serializer); $data = ['super_model' => 'model_val', 'car_size' => 'size_val', 'color' => 'color_val']; @@ -321,9 +323,9 @@ public function testSerializedNameDenormalize() */ public function testSerializedNameDenormalizeWhenIgnoringSerializedName() { - $serializer = $this->getMock(__NAMESPACE__.'\MetadataObjectSerializerNormalizer'); + $serializer = $this->getMockBuilder(__NAMESPACE__.'\MetadataObjectSerializerNormalizer')->getMock(); $classMetadataFactory = new ClassMetadataFactory(new BetterAnnotationLoader(new AnnotationReader())); - $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, null, new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); + $normalizer = new MetadataAwareNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), new MetadataAwarePropertyTypeExtractor($classMetadataFactory)); $normalizer->setSerializer($serializer); $data = ['model' => 'model_val', 'carSize' => 'size_val', 'color' => 'color_val']; From 05a0691b81546d2f22cdd9803a23874898197b65 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sun, 2 Sep 2018 11:50:05 +0200 Subject: [PATCH 13/13] Typo --- src/Symfony/Component/Serializer/phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/phpunit.xml.dist b/src/Symfony/Component/Serializer/phpunit.xml.dist index 93d2719fdbd90..ce9af71d90a7c 100644 --- a/src/Symfony/Component/Serializer/phpunit.xml.dist +++ b/src/Symfony/Component/Serializer/phpunit.xml.dist @@ -4,7 +4,7 @@ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" backupGlobals="false" colors="true" - bootstrap="Tests/autoload.php" + bootstrap="vendor/autoload.php" failOnRisky="true" failOnWarning="true" >