Skip to content

Commit b0813fd

Browse files
committed
[Serializer] Refactoring and object_to_populate support.
1 parent 6b606b5 commit b0813fd

File tree

4 files changed

+109
-71
lines changed

4 files changed

+109
-71
lines changed

Normalizer/AbstractNormalizer.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,88 @@ protected function getAllowedAttributes($classOrObject, array $context)
127127

128128
return array_unique($allowedAttributes);
129129
}
130+
131+
/**
132+
* Normalizes the given data to an array. It's particularly useful during
133+
* the denormalization process.
134+
*
135+
* @param object|array $data
136+
*
137+
* @return array
138+
*/
139+
protected function prepareForDenormalization($data)
140+
{
141+
if (is_array($data) || is_object($data) && $data instanceof \ArrayAccess) {
142+
$normalizedData = $data;
143+
} elseif (is_object($data)) {
144+
$normalizedData = array();
145+
146+
foreach ($data as $attribute => $value) {
147+
$normalizedData[$attribute] = $value;
148+
}
149+
} else {
150+
$normalizedData = array();
151+
}
152+
153+
return $normalizedData;
154+
}
155+
156+
/**
157+
* Instantiates an object using contructor parameters when needed.
158+
*
159+
* This method also allows to denormalize data into an existing object if
160+
* it is present in the context with the object_to_populate key.
161+
*
162+
* @param array $data
163+
* @param string $class
164+
* @param array $context
165+
* @param \ReflectionClass $reflectionClass
166+
* @param array|bool $allowedAttributes
167+
*
168+
* @return object
169+
*
170+
* @throws RuntimeException
171+
*/
172+
protected function instantiateObject(array $data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
173+
{
174+
if (
175+
isset($context['object_to_populate']) &&
176+
is_object($context['object_to_populate']) &&
177+
$class === get_class($context['object_to_populate'])
178+
) {
179+
return $context['object_to_populate'];
180+
}
181+
182+
$constructor = $reflectionClass->getConstructor();
183+
if ($constructor) {
184+
$constructorParameters = $constructor->getParameters();
185+
186+
$params = array();
187+
foreach ($constructorParameters as $constructorParameter) {
188+
$paramName = lcfirst($this->formatAttribute($constructorParameter->name));
189+
190+
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
191+
$ignored = in_array($paramName, $this->ignoredAttributes);
192+
if ($allowed && !$ignored && isset($data[$paramName])) {
193+
$params[] = $data[$paramName];
194+
// don't run set for a parameter passed to the constructor
195+
unset($data[$paramName]);
196+
} elseif ($constructorParameter->isOptional()) {
197+
$params[] = $constructorParameter->getDefaultValue();
198+
} else {
199+
throw new RuntimeException(
200+
sprintf(
201+
'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.',
202+
$class,
203+
$constructorParameter->name
204+
)
205+
);
206+
}
207+
}
208+
209+
return $reflectionClass->newInstanceArgs($params);
210+
}
211+
212+
return new $class();
213+
}
130214
}

Normalizer/GetSetMethodNormalizer.php

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -136,54 +136,16 @@ public function normalize($object, $format = null, array $context = array())
136136

137137
/**
138138
* {@inheritdoc}
139+
*
140+
* @throws RuntimeException
139141
*/
140142
public function denormalize($data, $class, $format = null, array $context = array())
141143
{
142144
$allowedAttributes = $this->getAllowedAttributes($class, $context);
143-
144-
if (is_array($data) || is_object($data) && $data instanceof \ArrayAccess) {
145-
$normalizedData = $data;
146-
} elseif (is_object($data)) {
147-
$normalizedData = array();
148-
149-
foreach ($data as $attribute => $value) {
150-
$normalizedData[$attribute] = $value;
151-
}
152-
} else {
153-
$normalizedData = array();
154-
}
145+
$normalizedData = $this->prepareForDenormalization($data);
155146

156147
$reflectionClass = new \ReflectionClass($class);
157-
$constructor = $reflectionClass->getConstructor();
158-
159-
if ($constructor) {
160-
$constructorParameters = $constructor->getParameters();
161-
162-
$params = array();
163-
foreach ($constructorParameters as $constructorParameter) {
164-
$paramName = lcfirst($this->formatAttribute($constructorParameter->name));
165-
166-
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
167-
$ignored = in_array($paramName, $this->ignoredAttributes);
168-
if ($allowed && !$ignored && isset($normalizedData[$paramName])) {
169-
$params[] = $normalizedData[$paramName];
170-
// don't run set for a parameter passed to the constructor
171-
unset($normalizedData[$paramName]);
172-
} elseif ($constructorParameter->isOptional()) {
173-
$params[] = $constructorParameter->getDefaultValue();
174-
} else {
175-
throw new RuntimeException(
176-
'Cannot create an instance of '.$class.
177-
' from serialized data because its constructor requires '.
178-
'parameter "'.$constructorParameter->name.
179-
'" to be present.');
180-
}
181-
}
182-
183-
$object = $reflectionClass->newInstanceArgs($params);
184-
} else {
185-
$object = new $class();
186-
}
148+
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
187149

188150
foreach ($normalizedData as $attribute => $value) {
189151
$allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes);

Normalizer/PropertyNormalizer.php

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,41 +72,16 @@ public function normalize($object, $format = null, array $context = array())
7272

7373
/**
7474
* {@inheritdoc}
75+
*
76+
* @throws RuntimeException
7577
*/
7678
public function denormalize($data, $class, $format = null, array $context = array())
7779
{
7880
$allowedAttributes = $this->getAllowedAttributes($class, $context);
81+
$data = $this->prepareForDenormalization($data);
7982

8083
$reflectionClass = new \ReflectionClass($class);
81-
$constructor = $reflectionClass->getConstructor();
82-
83-
if ($constructor) {
84-
$constructorParameters = $constructor->getParameters();
85-
86-
$params = array();
87-
foreach ($constructorParameters as $constructorParameter) {
88-
$paramName = lcfirst($this->formatAttribute($constructorParameter->name));
89-
90-
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
91-
$ignored = in_array($paramName, $this->ignoredAttributes);
92-
if ($allowed && !$ignored && isset($data[$paramName])) {
93-
$params[] = $data[$paramName];
94-
// don't run set for a parameter passed to the constructor
95-
unset($data[$paramName]);
96-
} elseif (!$constructorParameter->isOptional()) {
97-
throw new RuntimeException(sprintf(
98-
'Cannot create an instance of %s from serialized data because '.
99-
'its constructor requires parameter "%s" to be present.',
100-
$class,
101-
$constructorParameter->name
102-
));
103-
}
104-
}
105-
106-
$object = $reflectionClass->newInstanceArgs($params);
107-
} else {
108-
$object = new $class();
109-
}
84+
$object = $this->instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes);
11085

11186
foreach ($data as $propertyName => $value) {
11287
$propertyName = lcfirst($this->formatAttribute($propertyName));

Tests/Normalizer/GetSetMethodNormalizerTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,23 @@ public function testCircularReferenceHandler()
385385
$expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy');
386386
$this->assertEquals($expected, $this->normalizer->normalize($obj));
387387
}
388+
389+
public function testObjectToPopulate()
390+
{
391+
$dummy = new GetSetDummy();
392+
$dummy->setFoo('foo');
393+
394+
$obj = $this->normalizer->denormalize(
395+
array('bar' => 'bar'),
396+
__NAMESPACE__.'\GetSetDummy',
397+
null,
398+
array('object_to_populate' => $dummy)
399+
);
400+
401+
$this->assertEquals($dummy, $obj);
402+
$this->assertEquals('foo', $obj->getFoo());
403+
$this->assertEquals('bar', $obj->getBar());
404+
}
388405
}
389406

390407
class GetSetDummy

0 commit comments

Comments
 (0)