Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9feec3d

Browse filesBrowse files
committed
feature #17191 [Serializer] Move the normalization logic in an abstract class (dunglas)
This PR was squashed before being merged into the 3.1-dev branch (closes #17191). Discussion ---------- [Serializer] Move the normalization logic in an abstract class | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a As suggested by @xabbuh, move all the normalization logic for objects in an abstract class. It will ease the maintenance as well as adding new features such as #17113 and #16143. I've introduced a new abstract class to avoid BC breaks in `AbstractNormalizer`. As a (good) side effect, all normalizers now benefits from the caching system introduced in #16547. Commits ------- 3bec813 [Serializer] Move the normalization logic in an abstract class
2 parents 7f964a9 + 3bec813 commit 9feec3d
Copy full SHA for 9feec3d

File tree

5 files changed

+353
-266
lines changed
Filter options

5 files changed

+353
-266
lines changed

‎src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+22-2Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,34 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
199199

200200
$allowedAttributes = array();
201201
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
202-
if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
203-
$allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
202+
$name = $attributeMetadata->getName();
203+
204+
if (
205+
count(array_intersect($attributeMetadata->getGroups(), $context['groups'])) &&
206+
$this->isAllowedAttribute($classOrObject, $name, null, $context)
207+
) {
208+
$allowedAttributes[] = $attributesAsString ? $name : $attributeMetadata;
204209
}
205210
}
206211

207212
return $allowedAttributes;
208213
}
209214

215+
/**
216+
* Is this attribute allowed?
217+
*
218+
* @param object|string $classOrObject
219+
* @param string $attribute
220+
* @param string|null $format
221+
* @param array $context
222+
*
223+
* @return bool
224+
*/
225+
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
226+
{
227+
return !in_array($attribute, $this->ignoredAttributes);
228+
}
229+
210230
/**
211231
* Normalizes the given data to an array. It's particularly useful during
212232
* the denormalization process.
+167Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Normalizer;
13+
14+
use Symfony\Component\Serializer\Exception\CircularReferenceException;
15+
use Symfony\Component\Serializer\Exception\LogicException;
16+
17+
/**
18+
* Base class for a normalizer dealing with objects.
19+
*
20+
* @author Kévin Dunglas <dunglas@gmail.com>
21+
*/
22+
abstract class AbstractObjectNormalizer extends AbstractNormalizer
23+
{
24+
private $attributesCache = array();
25+
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function supportsNormalization($data, $format = null)
30+
{
31+
return is_object($data) && !$data instanceof \Traversable;
32+
}
33+
34+
/**
35+
* {@inheritdoc}
36+
*
37+
* @throws CircularReferenceException
38+
*/
39+
public function normalize($object, $format = null, array $context = array())
40+
{
41+
if ($this->isCircularReference($object, $context)) {
42+
return $this->handleCircularReference($object);
43+
}
44+
45+
$data = array();
46+
$attributes = $this->getAttributes($object, $format, $context);
47+
48+
foreach ($attributes as $attribute) {
49+
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
50+
51+
if (isset($this->callbacks[$attribute])) {
52+
$attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue);
53+
}
54+
55+
if (null !== $attributeValue && !is_scalar($attributeValue)) {
56+
if (!$this->serializer instanceof NormalizerInterface) {
57+
throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attribute));
58+
}
59+
60+
$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
61+
}
62+
63+
if ($this->nameConverter) {
64+
$attribute = $this->nameConverter->normalize($attribute);
65+
}
66+
67+
$data[$attribute] = $attributeValue;
68+
}
69+
70+
return $data;
71+
}
72+
73+
/**
74+
* Gets and caches attributes for the given object, format and context.
75+
*
76+
* @param object $object
77+
* @param string|null $format
78+
* @param array $context
79+
*
80+
* @return string[]
81+
*/
82+
protected function getAttributes($object, $format = null, array $context)
83+
{
84+
$key = sprintf('%s-%s', get_class($object), serialize($context));
85+
86+
if (isset($this->attributesCache[$key])) {
87+
return $this->attributesCache[$key];
88+
}
89+
90+
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
91+
92+
if (false !== $allowedAttributes) {
93+
return $this->attributesCache[$key] = $allowedAttributes;
94+
}
95+
96+
return $this->attributesCache[$key] = $this->extractAttributes($object, $format, $context);
97+
}
98+
99+
/**
100+
* Extracts attributes to normalize from the class of the given object, format and context.
101+
*
102+
* @param object $object
103+
* @param string|null $format
104+
* @param array $context
105+
*
106+
* @return string[]
107+
*/
108+
abstract protected function extractAttributes($object, $format = null, array $context = array());
109+
110+
/**
111+
* Gets the attribute value.
112+
*
113+
* @param object $object
114+
* @param string $attribute
115+
* @param string|null $format
116+
* @param array $context
117+
*
118+
* @return mixed
119+
*/
120+
abstract protected function getAttributeValue($object, $attribute, $format = null, array $context = array());
121+
122+
/**
123+
* {@inheritdoc}
124+
*/
125+
public function supportsDenormalization($data, $type, $format = null)
126+
{
127+
return class_exists($type);
128+
}
129+
130+
/**
131+
* {@inheritdoc}
132+
*/
133+
public function denormalize($data, $class, $format = null, array $context = array())
134+
{
135+
$allowedAttributes = $this->getAllowedAttributes($class, $context, true);
136+
$normalizedData = $this->prepareForDenormalization($data);
137+
138+
$reflectionClass = new \ReflectionClass($class);
139+
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
140+
141+
foreach ($normalizedData as $attribute => $value) {
142+
if ($this->nameConverter) {
143+
$attribute = $this->nameConverter->denormalize($attribute);
144+
}
145+
146+
$allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes);
147+
$ignored = in_array($attribute, $this->ignoredAttributes);
148+
149+
if ($allowed && !$ignored) {
150+
$this->setAttributeValue($object, $attribute, $value, $format, $context);
151+
}
152+
}
153+
154+
return $object;
155+
}
156+
157+
/**
158+
* Sets attribute value.
159+
*
160+
* @param object $object
161+
* @param string $attribute
162+
* @param mixed $value
163+
* @param string|null $format
164+
* @param array $context
165+
*/
166+
abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array());
167+
}

‎src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
+57-56Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14-
use Symfony\Component\Serializer\Exception\CircularReferenceException;
15-
use Symfony\Component\Serializer\Exception\LogicException;
1614
use Symfony\Component\Serializer\Exception\RuntimeException;
1715

1816
/**
@@ -36,59 +34,8 @@
3634
* @author Nils Adermann <naderman@naderman.de>
3735
* @author Kévin Dunglas <dunglas@gmail.com>
3836
*/
39-
class GetSetMethodNormalizer extends AbstractNormalizer
37+
class GetSetMethodNormalizer extends AbstractObjectNormalizer
4038
{
41-
/**
42-
* {@inheritdoc}
43-
*
44-
* @throws LogicException
45-
* @throws CircularReferenceException
46-
*/
47-
public function normalize($object, $format = null, array $context = array())
48-
{
49-
if ($this->isCircularReference($object, $context)) {
50-
return $this->handleCircularReference($object);
51-
}
52-
53-
$reflectionObject = new \ReflectionObject($object);
54-
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
55-
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
56-
57-
$attributes = array();
58-
foreach ($reflectionMethods as $method) {
59-
if ($this->isGetMethod($method)) {
60-
$attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3));
61-
if (in_array($attributeName, $this->ignoredAttributes)) {
62-
continue;
63-
}
64-
65-
if (false !== $allowedAttributes && !in_array($attributeName, $allowedAttributes)) {
66-
continue;
67-
}
68-
69-
$attributeValue = $method->invoke($object);
70-
if (isset($this->callbacks[$attributeName])) {
71-
$attributeValue = call_user_func($this->callbacks[$attributeName], $attributeValue);
72-
}
73-
if (null !== $attributeValue && !is_scalar($attributeValue)) {
74-
if (!$this->serializer instanceof NormalizerInterface) {
75-
throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attributeName));
76-
}
77-
78-
$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
79-
}
80-
81-
if ($this->nameConverter) {
82-
$attributeName = $this->nameConverter->normalize($attributeName);
83-
}
84-
85-
$attributes[$attributeName] = $attributeValue;
86-
}
87-
}
88-
89-
return $attributes;
90-
}
91-
9239
/**
9340
* {@inheritdoc}
9441
*
@@ -128,15 +75,15 @@ public function denormalize($data, $class, $format = null, array $context = arra
12875
*/
12976
public function supportsNormalization($data, $format = null)
13077
{
131-
return is_object($data) && !$data instanceof \Traversable && $this->supports(get_class($data));
78+
return parent::supportsNormalization($data, $format) && $this->supports(get_class($data));
13279
}
13380

13481
/**
13582
* {@inheritdoc}
13683
*/
13784
public function supportsDenormalization($data, $type, $format = null)
13885
{
139-
return class_exists($type) && $this->supports($type);
86+
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
14087
}
14188

14289
/**
@@ -179,4 +126,58 @@ private function isGetMethod(\ReflectionMethod $method)
179126
)
180127
;
181128
}
129+
130+
/**
131+
* {@inheritdoc}
132+
*/
133+
protected function extractAttributes($object, $format = null, array $context = array())
134+
{
135+
$reflectionObject = new \ReflectionObject($object);
136+
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
137+
138+
$attributes = array();
139+
foreach ($reflectionMethods as $method) {
140+
if (!$this->isGetMethod($method)) {
141+
continue;
142+
}
143+
144+
$attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3));
145+
146+
if ($this->isAllowedAttribute($object, $attributeName)) {
147+
$attributes[] = $attributeName;
148+
}
149+
}
150+
151+
return $attributes;
152+
}
153+
154+
/**
155+
* {@inheritdoc}
156+
*/
157+
protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
158+
{
159+
$ucfirsted = ucfirst($attribute);
160+
161+
$getter = 'get'.$ucfirsted;
162+
if (is_callable(array($object, $getter))) {
163+
return $object->$getter();
164+
}
165+
166+
$isser = 'is'.$ucfirsted;
167+
if (is_callable(array($object, $isser))) {
168+
return $object->$isser();
169+
}
170+
}
171+
172+
/**
173+
* {@inheritdoc}
174+
*/
175+
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
176+
{
177+
$setter = 'set'.ucfirst($attribute);
178+
179+
if (is_callable(array($object, $setter))) {
180+
$object->$setter($value);
181+
}
182+
}
182183
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.