Browse Source

Merge remote branch 'Seldaek/serializer'

* Seldaek/serializer:
  [Serializer] CS fixes
  [Serializer] Split supports in supportsNormalization and supportsDenormalization
  [Serializer] Add support for Traversable objects
  Fixed docs typo
  [Serializer] updated SerializerInterface
Fabien Potencier 14 years ago
parent
commit
c98bf4ff94

+ 5 - 2
UPDATE.md

@@ -124,6 +124,9 @@ beta1 to beta2
           'allow_delete' => true,
       ));
 
+* Serializer: The NormalizerInterface's `supports()` method has been split in
+  two methods: `supportsNormalization` and `supportsDenormalization`.
+
 PR12 to beta1
 -------------
 
@@ -210,11 +213,11 @@ PR11 to PR12
         <app:engine>twig</app:engine>
         <twig:extension>twig.extension.debug</twig:extension>
 
-* Fixes a critical security issue which allowed all users to switch to 
+* Fixes a critical security issue which allowed all users to switch to
   arbitrary accounts when the SwitchUserListener was activated. Configurations
   which do not use the SwitchUserListener are not affected.
 
-* The Dependency Injection Container now strongly validates the references of 
+* The Dependency Injection Container now strongly validates the references of
   all your services at the end of its compilation process. If you have invalid
   references this will result in a compile-time exception instead of a run-time
   exception (the previous behavior).

+ 6 - 6
src/Symfony/Component/Serializer/Encoder/XmlEncoder.php

@@ -224,14 +224,14 @@ class XmlEncoder extends AbstractEncoder implements DecoderInterface
                     $append = $this->selectNodeType($parentNode, $data);
                 } elseif (is_array($data) && false === is_numeric($key)) {
                     /**
-                    * Is this array fully numeric keys?
-                    */
+                     * Is this array fully numeric keys?
+                     */
                     if (ctype_digit(implode('', array_keys($data)))) {
                         /**
-                        * Create nodes to append to $parentNode based on the $key of this array
-                        * Produces <xml><item>0</item><item>1</item></xml>
-                        * From array("item" => array(0,1));
-                        */
+                         * Create nodes to append to $parentNode based on the $key of this array
+                         * Produces <xml><item>0</item><item>1</item></xml>
+                         * From array("item" => array(0,1));
+                         */
                         foreach ($data as $subData) {
                             $append = $this->appendNode($parentNode, $subData, $key);
                         }

+ 19 - 5
src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php

@@ -39,13 +39,27 @@ class CustomNormalizer extends AbstractNormalizer
     /**
      * Checks if the given class implements the NormalizableInterface.
      *
-     * @param ReflectionClass $class  A ReflectionClass instance of the class
-     *                                to serialize into or from.
-     * @param string          $format The format being (de-)serialized from or into.
+     * @param mixed   $data   Data to normalize.
+     * @param string  $format The format being (de-)serialized from or into.
      * @return Boolean
      */
-    public function supports(\ReflectionClass $class, $format = null)
+    public function supportsNormalization($data, $format = null)
     {
-        return $class->implementsInterface('Symfony\Component\Serializer\Normalizer\NormalizableInterface');
+        return $data instanceof NormalizableInterface;
+    }
+
+    /**
+     * Checks if the given class implements the NormalizableInterface.
+     *
+     * @param mixed   $data   Data to denormalize from.
+     * @param string  $type   The class to which the data should be denormalized.
+     * @param string  $format The format being deserialized from.
+     * @return Boolean
+     */
+    public function supportsDenormalization($data, $type, $format = null)
+    {
+        $class = new \ReflectionClass($type);
+
+        return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\NormalizableInterface');
     }
 }

+ 20 - 6
src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

@@ -107,23 +107,37 @@ class GetSetMethodNormalizer extends AbstractNormalizer
         return $object;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function supportsNormalization($data, $format = null)
+    {
+        return $this->supports(get_class($data));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function supportsDenormalization($data, $type, $format = null)
+    {
+        return $this->supports($type);
+    }
+
     /**
      * Checks if the given class has any get{Property} method.
      *
-     * @param ReflectionClass $class  A ReflectionClass instance of the class
-     *                                to serialize into or from.
-     * @param string          $format The format being (de-)serialized from or into.
-     * @return Boolean Whether the class has any getters.
+     * @param string $class
+     * @return Boolean
      */
-    public function supports(\ReflectionClass $class, $format = null)
+    private function supports($class)
     {
+        $class = new \ReflectionClass($type);
         $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
         foreach ($methods as $method) {
             if ($this->isGetMethod($method)) {
                 return true;
             }
         }
-
         return false;
     }
 

+ 15 - 6
src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php

@@ -14,7 +14,7 @@ use Symfony\Component\Serializer\SerializerInterface;
  */
 
 /**
- * Defines the interface of serializers
+ * Defines the interface of normalizers.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
@@ -43,16 +43,25 @@ interface NormalizerInterface
     function denormalize($data, $class, $format = null);
 
     /**
-     * Checks whether the given class is supported by this normalizer
-     *
-     * @param ReflectionClass $class
-     * @param string          $format format the given data was extracted from
+     * Checks whether the given class is supported for normalization by this normalizer
      *
+     * @param mixed   $data   Data to normalize.
+     * @param string  $format The format being (de-)serialized from or into.
      * @return Boolean
+     * @api
+     */
+    function supportsNormalization($data, $format = null);
+
+    /**
+     * Checks whether the given class is supported for denormalization by this normalizer
      *
+     * @param mixed   $data   Data to denormalize from.
+     * @param string  $type   The class to which the data should be denormalized.
+     * @param string  $format The format being deserialized from.
+     * @return Boolean
      * @api
      */
-    function supports(\ReflectionClass $class, $format = null);
+    function supportsDenormalization($data, $type, $format = null);
 
     /**
      * Sets the owning Serializer object

+ 36 - 12
src/Symfony/Component/Serializer/Serializer.php

@@ -31,6 +31,7 @@ class Serializer implements SerializerInterface
     private $normalizers = array();
     private $encoders = array();
     protected $normalizerCache = array();
+    protected $denormalizerCache = array();
 
     /**
      * @param mixed $value value to test
@@ -46,7 +47,14 @@ class Serializer implements SerializerInterface
      */
     public function serialize($data, $format)
     {
-        return $this->encode($data, $format);
+        return $this->encode($this->normalize($data, $format), $format);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function deserialize($data, $type, $format) {
+        return $this->denormalize($this->decode($data, $format), $type, $format);
     }
 
     /**
@@ -61,9 +69,8 @@ class Serializer implements SerializerInterface
         if (isset($this->normalizerCache[$class][$format])) {
             return $this->normalizerCache[$class][$format]->normalize($object, $format, $properties);
         }
-        $reflClass = new \ReflectionClass($class);
         foreach ($this->normalizers as $normalizer) {
-            if ($normalizer->supports($reflClass, $format)) {
+            if ($normalizer->supportsNormalization($object, $class, $format)) {
                 $this->normalizerCache[$class][$format] = $normalizer;
                 return $normalizer->normalize($object, $format, $properties);
             }
@@ -79,13 +86,12 @@ class Serializer implements SerializerInterface
         if (!$this->normalizers) {
             throw new \LogicException('You must register at least one normalizer to be able to denormalize objects.');
         }
-        if (isset($this->normalizerCache[$class][$format])) {
-            return $this->normalizerCache[$class][$format]->denormalize($data, $class, $format);
+        if (isset($this->denormalizerCache[$class][$format])) {
+            return $this->denormalizerCache[$class][$format]->denormalize($data, $class, $format);
         }
-        $reflClass = new \ReflectionClass($class);
         foreach ($this->normalizers as $normalizer) {
-            if ($normalizer->supports($reflClass, $format)) {
-                $this->normalizerCache[$class][$format] = $normalizer;
+            if ($normalizer->supportsDenormalization($class, $format)) {
+                $this->denormalizerCache[$class][$format] = $normalizer;
                 return $normalizer->denormalize($data, $class, $format);
             }
         }
@@ -95,20 +101,38 @@ class Serializer implements SerializerInterface
     /**
      * {@inheritdoc}
      */
-    public function normalize($data, $format)
+    public function normalize($data, $format = null)
     {
-        if (is_array($data)) {
+        if (!$this->isStructuredType($data)) {
+            return $data;
+        }
+        if ($data instanceof Traversable) {
+            $normalized = array();
             foreach ($data as $key => $val) {
-                $data[$key] = $this->isStructuredType($val) ? $this->normalize($val, $format) : $val;
+                $normalized[$key] = $this->normalize($val, $format);
             }
-            return $data;
+            return $normalized;
         }
         if (is_object($data)) {
             return $this->normalizeObject($data, $format);
         }
+        if (is_array($data)) {
+            foreach ($data as $key => $val) {
+                $data[$key] = $this->normalize($val, $format);
+            }
+            return $data;
+        }
         throw new \UnexpectedValueException('An unexpected value could not be normalized: '.var_export($data, true));
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function denormalize($data, $type, $format = null)
+    {
+        return $this->denormalizeObject($data, $type, $format);
+    }
+
     /**
      * {@inheritdoc}
      */

+ 16 - 60
src/Symfony/Component/Serializer/SerializerInterface.php

@@ -32,34 +32,34 @@ interface SerializerInterface
     function serialize($data, $format);
 
     /**
-     * Normalizes any data into a set of arrays/scalars
+     * Deserializes data into the given type.
      *
-     * @param mixed $data data to normalize
-     * @param string $format format name, present to give the option to normalizers to act differently based on formats
-     * @return array|scalar
-     * @api
+     * @param mixed $data
+     * @param string $type
+     * @param string $format
      */
-    function normalize($data, $format);
+    function deserialize($data, $type, $format);
 
     /**
-     * Normalizes an object into a set of arrays/scalars
+     * Normalizes any data into a set of arrays/scalars
      *
-     * @param object $object object to normalize
+     * @param mixed $data data to normalize
      * @param string $format format name, present to give the option to normalizers to act differently based on formats
-     * @param array $properties a list of properties to extract, if null all properties are returned
      * @return array|scalar
+     * @api
      */
-    function normalizeObject($object, $format, $properties = null);
+    function normalize($data, $format = null);
 
     /**
-     * Denormalizes data back into an object of the given class
+     * Denormalizes data into the given type.
      *
-     * @param mixed $data data to restore
-     * @param string $class the expected class to instantiate
-     * @param string $format format name, present to give the option to normalizers to act differently based on formats
-     * @return object
+     * @param mixed $data
+     * @param string $type
+     * @param string $format
+     *
+     * @return mixed
      */
-    function denormalizeObject($data, $class, $format = null);
+    function denormalize($data, $type, $format = null);
 
     /**
      * Encodes data into the given format
@@ -80,48 +80,4 @@ interface SerializerInterface
      * @api
      */
     function decode($data, $format);
-
-    /**
-     * @param NormalizerInterface $normalizer
-     */
-    function addNormalizer(NormalizerInterface $normalizer);
-
-    /**
-     * @return array[]NormalizerInterface
-     */
-    function getNormalizers();
-
-    /**
-     * @param NormalizerInterface $normalizer
-     */
-    function removeNormalizer(NormalizerInterface $normalizer);
-
-    /**
-     * @param string           $format  format name
-     * @param EncoderInterface $encoder
-     */
-    function setEncoder($format, EncoderInterface $encoder);
-
-    /**
-     * @return EncoderInterface
-     */
-    function getEncoders();
-
-    /**
-     * @return array[]EncoderInterface
-     */
-    function getEncoder($format);
-
-    /**
-     * Checks whether the serializer has an encoder registered for the given format
-     *
-     * @param string $format format name
-     * @return Boolean
-     */
-    function hasEncoder($format);
-
-    /**
-     * @param string $format format name
-     */
-    function removeEncoder($format);
 }

+ 9 - 3
tests/Symfony/Tests/Component/Serializer/Normalizer/CustomNormalizerTest.php

@@ -44,9 +44,15 @@ class CustomNormalizerTest extends \PHPUnit_Framework_TestCase
         $this->assertNull($obj->xmlFoo);
     }
 
-    public function testSupports()
+    public function testSupportsNormalization()
     {
-        $this->assertTrue($this->normalizer->supports(new \ReflectionClass(get_class(new ScalarDummy))));
-        $this->assertFalse($this->normalizer->supports(new \ReflectionClass('stdClass')));
+        $this->assertTrue($this->normalizer->supportsNormalization(new ScalarDummy));
+        $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass));
+    }
+
+    public function testSupportsDenormalization()
+    {
+        $this->assertTrue($this->normalizer->supportsDenormalization(array(), 'Symfony\Tests\Component\Serializer\Fixtures\ScalarDummy'));
+        $this->assertFalse($this->normalizer->supportsDenormalization(array(), 'stdClass'));
     }
 }

+ 7 - 3
tests/Symfony/Tests/Component/Serializer/Normalizer/GetSetMethodNormalizerTest.php

@@ -29,7 +29,8 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
         $obj->setBar('bar');
         $this->assertEquals(
             array('foo' => 'foo', 'bar' => 'bar', 'foobar' => 'foobar'),
-            $this->normalizer->normalize($obj, 'any'));
+            $this->normalizer->normalize($obj, 'any')
+        );
     }
 
     public function testNormalizeRestricted()
@@ -39,14 +40,17 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
         $obj->setBar('bar');
         $this->assertEquals(
             array('foo' => 'foo'),
-            $this->normalizer->normalize($obj, 'any', array('foo')));
+            $this->normalizer->normalize($obj, 'any', array('foo'))
+        );
     }
 
     public function testDenormalize()
     {
         $obj = $this->normalizer->denormalize(
             array('foo' => 'foo', 'bar' => 'bar', 'foobar' => 'foobar'),
-            __NAMESPACE__.'\GetSetDummy', 'any');
+            __NAMESPACE__.'\GetSetDummy',
+            'any'
+        );
         $this->assertEquals('foo', $obj->getFoo());
         $this->assertEquals('bar', $obj->getBar());
     }