Sfoglia il codice sorgente

merged branch ericclemmons/2295-serializer-normalizable-traversable (PR #2578)

Commits
-------

7346896 Changed Serialized#supportsNormalization to PRIVATE
e851efc Updated SerializerTest with "normalizeTraversable" & "testNormalizeGivesPriorityToInterfaceOverTraversable"
d789f94 Serializer#normalize gives precedence to objects that support normalization
9e6ba9a Added protected Serializer#supportsNormalization

Discussion
----------

[Serializer] `normalize` should use supported normalizer before Traversable

Bug fix: yes
Feature addition: no
Backwards compatibility break: no (discussion needed)
Symfony2 tests pass: yes
Fixes the following tickets: #2295

**Same as PR #2539, except rebased onto `2.0`**

Should I abstract out a `supportsDenormalization` function just for symmetry?
Fabien Potencier 13 anni fa
parent
commit
318576b3ac

+ 32 - 7
src/Symfony/Component/Serializer/Serializer.php

@@ -96,6 +96,9 @@ class Serializer implements SerializerInterface
         if (null === $data || is_scalar($data)) {
             return $data;
         }
+        if (is_object($data) && $this->supportsNormalization($data, $format)) {
+            return $this->normalizeObject($data, $format);
+        }
         if ($data instanceof \Traversable) {
             $normalized = array();
             foreach ($data as $key => $val) {
@@ -153,17 +156,14 @@ class Serializer implements SerializerInterface
         if (!$this->normalizers) {
             throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
         }
+
         $class = get_class($object);
-        if (isset($this->normalizerCache[$class][$format])) {
+
+        // If normalization is supported, cached normalizer will exist
+        if ($this->supportsNormalization($object, $format)) {
             return $this->normalizerCache[$class][$format]->normalize($object, $format);
         }
-        foreach ($this->normalizers as $normalizer) {
-            if ($normalizer->supportsNormalization($object, $class, $format)) {
-                $this->normalizerCache[$class][$format] = $normalizer;
 
-                return $normalizer->normalize($object, $format);
-            }
-        }
         throw new UnexpectedValueException('Could not normalize object of type '.$class.', no supporting normalizer found.');
     }
 
@@ -193,6 +193,31 @@ class Serializer implements SerializerInterface
         throw new UnexpectedValueException('Could not denormalize object of type '.$class.', no supporting normalizer found.');
     }
 
+    /**
+     * Check if normalizer cache or normalizers supports provided object, which will then be cached
+     *
+     * @param object $object Object to test for normalization support
+     * @param string $format Format name, needed for normalizers to pivot on
+     */
+    private function supportsNormalization($object, $format)
+    {
+        $class = get_class($object);
+
+        if (isset($this->normalizerCache[$class][$format])) {
+            return true;
+        }
+
+        foreach ($this->normalizers as $normalizer) {
+            if ($normalizer->supportsNormalization($object, $format)) {
+                $this->normalizerCache[$class][$format] = $normalizer;
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * {@inheritdoc}
      */

+ 25 - 0
tests/Symfony/Tests/Component/Serializer/Fixtures/NormalizableTraversableDummy.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Symfony\Tests\Component\Serializer\Fixtures;
+
+use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
+use Symfony\Component\Serializer\SerializerInterface;
+
+class NormalizableTraversableDummy extends TraversableDummy implements NormalizableInterface
+{
+    public function normalize(SerializerInterface $serializer, $format = null)
+    {
+        return array(
+            'foo' => 'normalizedFoo',
+            'bar' => 'normalizedBar',
+        );
+    }
+
+    public function denormalize(SerializerInterface $serializer, $data, $format = null)
+    {
+        return array(
+            'foo' => 'denormalizedFoo',
+            'bar' => 'denormalizedBar',
+        );
+    }
+}

+ 14 - 0
tests/Symfony/Tests/Component/Serializer/Fixtures/TraversableDummy.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Tests\Component\Serializer\Fixtures;
+
+class TraversableDummy implements \IteratorAggregate
+{
+    public $foo = 'foo';
+    public $bar = 'bar';
+
+    public function getIterator()
+    {
+        return new \ArrayIterator(get_object_vars($this));
+    }
+}

+ 17 - 0
tests/Symfony/Tests/Component/Serializer/SerializerTest.php

@@ -4,6 +4,9 @@ namespace Symfony\Tests\Component\Serializer;
 
 use Symfony\Component\Serializer\Serializer;
 use Symfony\Component\Serializer\Encoder\JsonEncoder;
+use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
+use Symfony\Tests\Component\Serializer\Fixtures\TraversableDummy;
+use Symfony\Tests\Component\Serializer\Fixtures\NormalizableTraversableDummy;
 
 /*
  * This file is part of the Symfony framework.
@@ -25,6 +28,20 @@ class SerializerTest extends \PHPUnit_Framework_TestCase
         $this->serializer->normalize(new \stdClass, 'xml');
     }
 
+    public function testNormalizeTraversable()
+    {
+        $this->serializer = new Serializer(array(), array('json' => new JsonEncoder()));
+        $result = $this->serializer->serialize(new TraversableDummy, 'json');
+        $this->assertEquals('{"foo":"foo","bar":"bar"}', $result);
+    }
+
+    public function testNormalizeGivesPriorityToInterfaceOverTraversable()
+    {
+        $this->serializer = new Serializer(array(new CustomNormalizer), array('json' => new JsonEncoder()));
+        $result = $this->serializer->serialize(new NormalizableTraversableDummy, 'json');
+        $this->assertEquals('{"foo":"normalizedFoo","bar":"normalizedBar"}', $result);
+    }
+
     /**
      * @expectedException \UnexpectedValueException
      */