From ffb1e40f20ae40636b04a327b1d41438d9195a30 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Wed, 22 Feb 2017 10:41:23 -0500 Subject: [PATCH 1/3] Pull Object to Populate Extraction Into a Trait And use that trait in `AbstractNormalizer`. --- .../Normalizer/AbstractNormalizer.php | 10 ++-- .../Normalizer/ObjectToPopulateTrait.php | 40 ++++++++++++++++ .../Normalizer/ObjectToPopulateTraitTest.php | 47 +++++++++++++++++++ 3 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php create mode 100644 src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 726654cbf6b87..5fcfdd20d252b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -27,6 +27,8 @@ */ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface { + use ObjectToPopulateTrait; + const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit'; const OBJECT_TO_POPULATE = 'object_to_populate'; const GROUPS = 'groups'; @@ -317,14 +319,8 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $format = null; } - if ( - isset($context[static::OBJECT_TO_POPULATE]) && - is_object($context[static::OBJECT_TO_POPULATE]) && - $context[static::OBJECT_TO_POPULATE] instanceof $class - ) { - $object = $context[static::OBJECT_TO_POPULATE]; + if (null != $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { unset($context[static::OBJECT_TO_POPULATE]); - return $object; } diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php b/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php new file mode 100644 index 0000000000000..65453f3eb285e --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.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\Normalizer; + +trait ObjectToPopulateTrait +{ + /** + * Extract the `object_to_populate` field from the context if it exists + * and is an instance of the provided $class. + * + * @param string $class The class the object should be + * @param $context The denormalization context + * @param string $key They in which to look for the object to populate. + * Keeps backwards compatability with `AbstractNormalizer. + * @return object|null An object if things check out, null otherwise. + */ + protected function extractObjectToPopulate($class, array $context, $key=null) + { + $key = $key ?: 'object_to_populate'; + + if ( + isset($context[$key]) && + is_object($context[$key]) && + $context[$key] instanceof $class + ) { + return $context[$key]; + } + + return null; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php new file mode 100644 index 0000000000000..aa924be0dc922 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php @@ -0,0 +1,47 @@ +extractObjectToPopulate(ProxyDummy::class, []); + + $this->assertNull($object); + } + + public function testExtractObjectToPopulateReturnsNullWhenNonObjectIsProvided() + { + $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + 'object_to_populate' => 'not an object', + ]); + + $this->assertNull($object); + } + + public function testExtractObjectToPopulateReturnsNullWhenTheClassIsNotAnInstanceOfTheProvidedClass() + { + $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + 'object_to_populate' => new \stdClass(), + ]); + + $this->assertNull($object); + } + + public function testExtractObjectToPopulateReturnsObjectWhenEverythingChecksOut() + { + $expected = new ProxyDummy(); + $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + 'object_to_populate' => $expected, + ]); + + $this->assertSame($expected, $object); + } +} From a556875029ad428d7a14b374ca7238d186c363f6 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Wed, 22 Feb 2017 10:47:36 -0500 Subject: [PATCH 2/3] Support object_to_populate in CustomNormalizer And fallback to the default `new $class();` behavior. --- .../Serializer/Normalizer/CustomNormalizer.php | 7 ++++--- .../Tests/Normalizer/CustomNormalizerTest.php | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index fd8bbca274211..b56b10f2dcf70 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -19,10 +19,11 @@ */ class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface { - private $cache = array(); - + use ObjectToPopulateTrait; use SerializerAwareTrait; + private $cache = array(); + /** * {@inheritdoc} */ @@ -36,7 +37,7 @@ public function normalize($object, $format = null, array $context = array()) */ public function denormalize($data, $class, $format = null, array $context = array()) { - $object = new $class(); + $object = $this->extractObjectToPopulate($class, $context) ?: new $class(); $object->denormalize($this->serializer, $data, $format, $context); return $object; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php index a3c3c1a0e62ec..a9263793ae46c 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php @@ -56,6 +56,18 @@ public function testDeserialize() $this->assertNull($obj->xmlFoo); } + public function testDenormalizeWithObjectToPopulateUsesProvidedObject() + { + $expected = new ScalarDummy(); + $obj = $this->normalizer->denormalize('foo', ScalarDummy::class, 'json', [ + 'object_to_populate' => $expected, + ]); + + $this->assertSame($expected, $obj); + $this->assertEquals('foo', $obj->foo); + $this->assertNull($obj->xmlFoo); + } + public function testSupportsNormalization() { $this->assertTrue($this->normalizer->supportsNormalization(new ScalarDummy())); From 75ad5b9d8362f6cd61a4cef1dd490a025f922799 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Wed, 22 Feb 2017 10:58:49 -0500 Subject: [PATCH 3/3] Apply Fixes from Fabbot --- .../Serializer/Normalizer/AbstractNormalizer.php | 3 ++- .../Normalizer/ObjectToPopulateTrait.php | 7 ++++--- .../Tests/Normalizer/CustomNormalizerTest.php | 4 ++-- .../Tests/Normalizer/ObjectToPopulateTraitTest.php | 14 +++++++------- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 5fcfdd20d252b..7a79949185740 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -319,8 +319,9 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $format = null; } - if (null != $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { + if (null !== $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { unset($context[static::OBJECT_TO_POPULATE]); + return $object; } diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php b/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php index 65453f3eb285e..f21100f014c6a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php @@ -20,10 +20,11 @@ trait ObjectToPopulateTrait * @param string $class The class the object should be * @param $context The denormalization context * @param string $key They in which to look for the object to populate. - * Keeps backwards compatability with `AbstractNormalizer. - * @return object|null An object if things check out, null otherwise. + * Keeps backwards compatibility with `AbstractNormalizer`. + * + * @return object|null an object if things check out, null otherwise */ - protected function extractObjectToPopulate($class, array $context, $key=null) + protected function extractObjectToPopulate($class, array $context, $key = null) { $key = $key ?: 'object_to_populate'; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php index a9263793ae46c..e5d0d445707eb 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php @@ -59,9 +59,9 @@ public function testDeserialize() public function testDenormalizeWithObjectToPopulateUsesProvidedObject() { $expected = new ScalarDummy(); - $obj = $this->normalizer->denormalize('foo', ScalarDummy::class, 'json', [ + $obj = $this->normalizer->denormalize('foo', ScalarDummy::class, 'json', array( 'object_to_populate' => $expected, - ]); + )); $this->assertSame($expected, $obj); $this->assertEquals('foo', $obj->foo); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php index aa924be0dc922..def71e5def5d7 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectToPopulateTraitTest.php @@ -12,25 +12,25 @@ class ObjectToPopulateTraitTest extends TestCase public function testExtractObjectToPopulateReturnsNullWhenKeyIsMissing() { - $object = $this->extractObjectToPopulate(ProxyDummy::class, []); + $object = $this->extractObjectToPopulate(ProxyDummy::class, array()); $this->assertNull($object); } public function testExtractObjectToPopulateReturnsNullWhenNonObjectIsProvided() { - $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + $object = $this->extractObjectToPopulate(ProxyDummy::class, array( 'object_to_populate' => 'not an object', - ]); + )); $this->assertNull($object); } public function testExtractObjectToPopulateReturnsNullWhenTheClassIsNotAnInstanceOfTheProvidedClass() { - $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + $object = $this->extractObjectToPopulate(ProxyDummy::class, array( 'object_to_populate' => new \stdClass(), - ]); + )); $this->assertNull($object); } @@ -38,9 +38,9 @@ public function testExtractObjectToPopulateReturnsNullWhenTheClassIsNotAnInstanc public function testExtractObjectToPopulateReturnsObjectWhenEverythingChecksOut() { $expected = new ProxyDummy(); - $object = $this->extractObjectToPopulate(ProxyDummy::class, [ + $object = $this->extractObjectToPopulate(ProxyDummy::class, array( 'object_to_populate' => $expected, - ]); + )); $this->assertSame($expected, $object); }