Skip to content

Commit 5895033

Browse files
committed
Merge branch '3.0'
* 3.0: (22 commits) [Yaml] Fix tests on PHP 7.0.2 [FrameworkBundle] Don't log twice with the error handler [2.7] Workaround https://bugs.php.net/63206 [2.3] Workaround https://bugs.php.net/63206 Add closing parenthesis [Serializer] Unset object_to_populate after using it Typo fix [WebProfiler] Fixed sf-minitoolbar height [HttpFoundation] Fixed Request HTTP_USER_AGENT on 3.X versions [2.3] Static Code Analysis for Components [Serializer] Use $context['cache_key'] to enhance caching Removed non-existent directory from composer autoload Fixed erroneous deprecation notice for extended Interfaces [Routing] cs fix Added support \IteratorAggregate for UniqueEntityValidator Update AbstractChoiceListTest.php [Serializer] Allow to use proxies in object_to_populate Fix #17306 Paths with % in it are note allowed (like urlencoded) Use proper class to fetch $versionStrategy property Added sort order SORT_STRING for params in UriSigner ... Conflicts: src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
2 parents 9a60c01 + 621c02e commit 5895033

9 files changed

+171
-22
lines changed

Normalizer/AbstractNormalizer.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,9 @@ protected function prepareForDenormalization($data)
244244
* Instantiates an object using constructor parameters when needed.
245245
*
246246
* This method also allows to denormalize data into an existing object if
247-
* it is present in the context with the object_to_populate key.
247+
* it is present in the context with the object_to_populate. This object
248+
* is removed from the context before being returned to avoid side effects
249+
* when recursively normalizing an object graph.
248250
*
249251
* @param array $data
250252
* @param string $class
@@ -261,9 +263,12 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
261263
if (
262264
isset($context['object_to_populate']) &&
263265
is_object($context['object_to_populate']) &&
264-
$class === get_class($context['object_to_populate'])
266+
$context['object_to_populate'] instanceof $class
265267
) {
266-
return $context['object_to_populate'];
268+
$object = $context['object_to_populate'];
269+
unset($context['object_to_populate']);
270+
271+
return $object;
267272
}
268273

269274
$constructor = $reflectionClass->getConstructor();

Normalizer/AbstractObjectNormalizer.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public function supportsNormalization($data, $format = null)
3838
*/
3939
public function normalize($object, $format = null, array $context = array())
4040
{
41+
if (!isset($context['cache_key'])) {
42+
$context['cache_key'] = $this->getCacheKey($context);
43+
}
4144
if ($this->isCircularReference($object, $context)) {
4245
return $this->handleCircularReference($object);
4346
}
@@ -81,7 +84,8 @@ public function normalize($object, $format = null, array $context = array())
8184
*/
8285
protected function getAttributes($object, $format = null, array $context)
8386
{
84-
$key = sprintf('%s-%s', get_class($object), serialize($context));
87+
$class = get_class($object);
88+
$key = $class.'-'.$context['cache_key'];
8589

8690
if (isset($this->attributesCache[$key])) {
8791
return $this->attributesCache[$key];
@@ -90,10 +94,18 @@ protected function getAttributes($object, $format = null, array $context)
9094
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
9195

9296
if (false !== $allowedAttributes) {
93-
return $this->attributesCache[$key] = $allowedAttributes;
97+
if ($context['cache_key']) {
98+
$this->attributesCache[$key] = $allowedAttributes;
99+
}
100+
101+
return $allowedAttributes;
102+
}
103+
104+
if (isset($this->attributesCache[$class])) {
105+
return $this->attributesCache[$class];
94106
}
95107

96-
return $this->attributesCache[$key] = $this->extractAttributes($object, $format, $context);
108+
return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context);
97109
}
98110

99111
/**
@@ -132,6 +144,9 @@ public function supportsDenormalization($data, $type, $format = null)
132144
*/
133145
public function denormalize($data, $class, $format = null, array $context = array())
134146
{
147+
if (!isset($context['cache_key'])) {
148+
$context['cache_key'] = $this->getCacheKey($context);
149+
}
135150
$allowedAttributes = $this->getAllowedAttributes($class, $context, true);
136151
$normalizedData = $this->prepareForDenormalization($data);
137152

@@ -164,4 +179,14 @@ public function denormalize($data, $class, $format = null, array $context = arra
164179
* @param array $context
165180
*/
166181
abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array());
182+
183+
private function getCacheKey(array $context)
184+
{
185+
try {
186+
return md5(serialize($context));
187+
} catch (\Exception $exception) {
188+
// The context cannot be serialized, skip the cache
189+
return false;
190+
}
191+
}
167192
}

Normalizer/ObjectNormalizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ protected function extractAttributes($object, $format = null, array $context = a
5656
continue;
5757
}
5858

59-
$name = $reflMethod->getName();
59+
$name = $reflMethod->name;
6060

61-
if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) {
61+
if (0 === strpos($name, 'get') || 0 === strpos($name, 'has')) {
6262
// getters and hassers
6363
$attributeName = lcfirst(substr($name, 3));
6464
} elseif (strpos($name, 'is') === 0) {
@@ -77,7 +77,7 @@ protected function extractAttributes($object, $format = null, array $context = a
7777
continue;
7878
}
7979

80-
$attributes[$reflProperty->getName()] = true;
80+
$attributes[$reflProperty->name] = true;
8181
}
8282

8383
return array_keys($attributes);

Serializer.php

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,10 @@ private function getNormalizer($data, $format)
187187
{
188188
if ($isObject = is_object($data)) {
189189
$class = get_class($data);
190-
if (isset($this->normalizerCache[$class][$format])) {
191-
return $this->normalizerCache[$class][$format];
192-
}
193190
}
194191

195192
foreach ($this->normalizers as $normalizer) {
196193
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format)) {
197-
if ($isObject) {
198-
$this->normalizerCache[$class][$format] = $normalizer;
199-
}
200-
201194
return $normalizer;
202195
}
203196
}
@@ -214,14 +207,8 @@ private function getNormalizer($data, $format)
214207
*/
215208
private function getDenormalizer($data, $class, $format)
216209
{
217-
if (isset($this->denormalizerCache[$class][$format])) {
218-
return $this->denormalizerCache[$class][$format];
219-
}
220-
221210
foreach ($this->normalizers as $normalizer) {
222211
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format)) {
223-
$this->denormalizerCache[$class][$format] = $normalizer;
224-
225212
return $normalizer;
226213
}
227214
}
@@ -264,6 +251,7 @@ private function normalizeObject($object, $format, array $context = array())
264251
if ($normalizer = $this->getNormalizer($object, $format)) {
265252
return $normalizer->normalize($object, $format, $context);
266253
}
254+
267255
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($object)));
268256
}
269257

@@ -289,6 +277,15 @@ private function denormalizeObject($data, $class, $format, array $context = arra
289277
if ($normalizer = $this->getDenormalizer($data, $class, $format)) {
290278
return $normalizer->denormalize($data, $class, $format, $context);
291279
}
280+
281+
foreach ($this->normalizers as $normalizer) {
282+
if ($normalizer instanceof DenormalizerInterface
283+
&& $normalizer->supportsDenormalization($data, $class, $format)) {
284+
285+
return $normalizer->denormalize($data, $class, $format, $context);
286+
}
287+
}
288+
292289
throw new UnexpectedValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $class));
293290
}
294291

Tests/Fixtures/ProxyDummy.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\Fixtures;
13+
14+
/**
15+
* @author Kévin Dunglas <[email protected]>
16+
*/
17+
class ProxyDummy extends ToBeProxyfiedDummy
18+
{
19+
}

Tests/Fixtures/ToBeProxyfiedDummy.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\Fixtures;
13+
14+
/**
15+
* @author Kévin Dunglas <[email protected]>
16+
*/
17+
class ToBeProxyfiedDummy
18+
{
19+
private $foo;
20+
21+
public function setFoo($foo)
22+
{
23+
$this->foo = $foo;
24+
}
25+
26+
public function getFoo()
27+
{
28+
return $this->foo;
29+
}
30+
}

Tests/Normalizer/AbstractNormalizerTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
66
use Symfony\Component\Serializer\Mapping\ClassMetadata;
77
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
8+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
89
use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy;
10+
use Symfony\Component\Serializer\Tests\Fixtures\ProxyDummy;
911

1012
/**
1113
* Provides a dummy Normalizer which extends the AbstractNormalizer.
@@ -88,4 +90,16 @@ public function testGetAllowedAttributesAsObjects()
8890
$result = $this->normalizer->getAllowedAttributes('c', array('groups' => array('other')), false);
8991
$this->assertEquals(array($a3, $a4), $result);
9092
}
93+
94+
public function testObjectToPopulateWithProxy()
95+
{
96+
$proxyDummy = new ProxyDummy();
97+
98+
$context = array('object_to_populate' => $proxyDummy);
99+
100+
$normalizer = new ObjectNormalizer();
101+
$normalizer->denormalize(array('foo' => 'bar'), 'Symfony\Component\Serializer\Tests\Fixtures\ToBeProxyfiedDummy', null, $context);
102+
103+
$this->assertSame('bar', $proxyDummy->getFoo());
104+
}
91105
}

Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,21 @@ public function testNormalizeStatic()
445445
{
446446
$this->assertEquals(array('foo' => 'K'), $this->normalizer->normalize(new ObjectWithStaticPropertiesAndMethods()));
447447
}
448+
449+
public function testNormalizeNotSerializableContext()
450+
{
451+
$objectDummy = new ObjectDummy();
452+
$expected = array(
453+
'foo' => null,
454+
'baz' => null,
455+
'fooBar' => '',
456+
'camelCase' => null,
457+
'object' => null,
458+
'bar' => null,
459+
);
460+
461+
$this->assertEquals($expected, $this->normalizer->normalize($objectDummy, null, array('not_serializable' => function () {})));
462+
}
448463
}
449464

450465
class ObjectDummy

Tests/SerializerTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,50 @@ public function testCustomNormalizerCanNormalizeCollectionsAndScalar()
9696
$this->assertNull($serializer->normalize('test'));
9797
}
9898

99+
public function testNormalizeWithSupportOnData()
100+
{
101+
$normalizer1 = $this->getMock('Symfony\Component\Serializer\Normalizer\NormalizerInterface');
102+
$normalizer1->method('supportsNormalization')
103+
->willReturnCallback(function ($data, $format) {
104+
return isset($data->test);
105+
});
106+
$normalizer1->method('normalize')->willReturn('test1');
107+
108+
$normalizer2 = $this->getMock('Symfony\Component\Serializer\Normalizer\NormalizerInterface');
109+
$normalizer2->method('supportsNormalization')
110+
->willReturn(true);
111+
$normalizer2->method('normalize')->willReturn('test2');
112+
113+
$serializer = new Serializer(array($normalizer1, $normalizer2));
114+
115+
$data = new \stdClass();
116+
$data->test = true;
117+
$this->assertEquals('test1', $serializer->normalize($data));
118+
119+
$this->assertEquals('test2', $serializer->normalize(new \stdClass()));
120+
}
121+
122+
public function testDenormalizeWithSupportOnData()
123+
{
124+
$denormalizer1 = $this->getMock('Symfony\Component\Serializer\Normalizer\DenormalizerInterface');
125+
$denormalizer1->method('supportsDenormalization')
126+
->willReturnCallback(function ($data, $type, $format) {
127+
return isset($data['test1']);
128+
});
129+
$denormalizer1->method('denormalize')->willReturn('test1');
130+
131+
$denormalizer2 = $this->getMock('Symfony\Component\Serializer\Normalizer\DenormalizerInterface');
132+
$denormalizer2->method('supportsDenormalization')
133+
->willReturn(true);
134+
$denormalizer2->method('denormalize')->willReturn('test2');
135+
136+
$serializer = new Serializer(array($denormalizer1, $denormalizer2));
137+
138+
$this->assertEquals('test1', $serializer->denormalize(array('test1' => true), 'test'));
139+
140+
$this->assertEquals('test2', $serializer->denormalize(array(), 'test'));
141+
}
142+
99143
public function testSerialize()
100144
{
101145
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));

0 commit comments

Comments
 (0)