Przeglądaj źródła

refactored some code

Johannes Schmitt 14 lat temu
rodzic
commit
566fccfcc4

+ 1 - 0
DependencyInjection/Configuration.php

@@ -43,6 +43,7 @@ class Configuration implements ConfigurationInterface
                                 ->end()
                             ->end()
                             ->booleanNode('doctrine_support')->defaultTrue()->end()
+                            ->booleanNode('normalizable_support')->defaultTrue()->end()
                         ->end()
                     ->end()
                 ->end()

+ 6 - 0
DependencyInjection/JMSSerializerExtension.php

@@ -51,6 +51,12 @@ class JMSSerializerExtension extends Extension
                 ->addTag('jms_serializer.normalizer')
             ;
         }
+        if ($config['normalization']['normalizable_support']) {
+            $container
+                ->getDefinition('jms_serializer.normalizable_object_normalizer')
+                ->addTag('jms_serializer.normalizer')
+            ;
+        }
 
         // versions
         if ($config['versions']) {

+ 5 - 0
Metadata/ClassHierarchyMetadata.php

@@ -32,6 +32,11 @@ class ClassHierarchyMetadata
         return $this->classes;
     }
 
+    public function getOutsideClass()
+    {
+        return end($this->classes);
+    }
+
     public function getLastModified()
     {
         $time = 0;

+ 14 - 0
Resources/config/services.xml

@@ -18,6 +18,9 @@
         <parameter key="jms_serializer.native_php_type_normalizer.class">JMS\SerializerBundle\Serializer\Normalizer\NativePhpTypeNormalizer</parameter>
         <parameter key="jms_serializer.property_based_normalizer.class">JMS\SerializerBundle\Serializer\Normalizer\PropertyBasedNormalizer</parameter>
         <parameter key="jms_serializer.array_collection_normalizer.class">JMS\SerializerBundle\Serializer\Normalizer\ArrayCollectionNormalizer</parameter>
+        <parameter key="jms_serializer.normalizable_object_normalizer.class">JMS\SerializerBundle\Serializer\Normalizer\NormalizableObjectNormalizer</parameter>
+
+        <parameter key="jms_serializer.unserialize_instance_creator.class">JMS\SerializerBundle\Serializer\UnserializeInstanceCreator</parameter>
 
         <parameter key="jms_serializer.version_exclusion_strategy.class">JMS\SerializerBundle\Serializer\Exclusion\VersionExclusionStrategy</parameter>
         <parameter key="jms_serializer.all_exclusion_strategy.class">JMS\SerializerBundle\Serializer\Exclusion\AllExclusionStrategy</parameter>
@@ -80,6 +83,10 @@
         </service>
         <service id="jms_serializer.naming_strategy" alias="jms_serializer.serialized_name_annotation_strategy" public="false" />
 
+        <!-- Instance Creators -->
+        <service id="jms_serializer.unserialize_instance_creator" class="%jms_serializer.unserialize_instance_creator.class%" public="false" />
+        <service id="jms_serializer.instance_creator" alias="jms_serializer.unserialize_instance_creator" public="false" />
+
         <!-- Normalizers -->
         <service id="jms_serializer.native_php_type_normalizer"
                  class="%jms_serializer.native_php_type_normalizer.class%"
@@ -90,11 +97,18 @@
                  public="false">
             <argument type="service" id="jms_serializer.metadata.metadata_factory" />
             <argument type="service" id="jms_serializer.naming_strategy" />
+            <argument type="service" id="jms_serializer.instance_creator" />
         </service>
         <service id="jms_serializer.array_collection_normalizer"
                  class="%jms_serializer.array_collection_normalizer.class%"
                  scope="prototype"
                  public="false" />
+        <service id="jms_serializer.normalizable_object_normalizer"
+                 class="%jms_serializer.normalizable_object_normalizer.class%"
+                 public="false"
+                 scope="prototype">
+             <argument type="service" id="jms_serializer.instance_creator" />
+        </service>
         <service id="jms_serializer.default_normalizer"
                  parent="jms_serializer.property_based_normalizer">
             <argument type="service" id="jms_serializer.default_exclusion_strategy_factory" />

+ 8 - 0
Serializer/InstanceCreatorInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer;
+
+interface InstanceCreatorInterface
+{
+    function createInstance(\ReflectionClass $class);
+}

+ 1 - 1
Serializer/LazyLoadingSerializer.php

@@ -18,7 +18,7 @@
 
 namespace JMS\SerializerBundle\Serializer;
 
-use Symfony\Component\Serializer\SerializerAwareInterface;
+use JMS\SerializerBundle\Serializer\SerializerAwareInterface;
 
 /**
  * Serializer that supports lazy-loading encoders.

+ 1 - 1
Serializer/Normalizer/ArrayCollectionNormalizer.php

@@ -21,7 +21,7 @@ namespace JMS\SerializerBundle\Serializer\Normalizer;
 use JMS\SerializerBundle\Exception\InvalidArgumentException;
 use JMS\SerializerBundle\Exception\UnsupportedException;
 use Doctrine\Common\Collections\ArrayCollection;
-use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
+use JMS\SerializerBundle\Serializer\Normalizer\SerializerAwareNormalizer;
 
 /**
  * This normalizer is specifically designed for Doctrine's ArrayCollection.

+ 1 - 1
Serializer/Normalizer/NativePhpTypeNormalizer.php

@@ -20,7 +20,7 @@ namespace JMS\SerializerBundle\Serializer\Normalizer;
 
 use JMS\SerializerBundle\Exception\RuntimeException;
 use JMS\SerializerBundle\Exception\UnsupportedException;
-use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
+use JMS\SerializerBundle\Serializer\Normalizer\SerializerAwareNormalizer;
 
 /**
  * Normalizer for native PHP types.

+ 35 - 0
Serializer/Normalizer/NormalizableInterface.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\Normalizer;
+
+use Symfony\Component\Serializer\SerializerInterface;
+
+/**
+ * This interface can be implemented by domain objects if they contain the
+ * normalization logic themselves.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+interface NormalizableInterface
+{
+    /**
+     * Normalizes the implementing object.
+     *
+     * @param SerializerInterface $serializer
+     * @param string $format
+     *
+     * @return mixed
+     */
+    function normalize(SerializerInterface $serializer, $format = null);
+
+    /**
+     * Denormalizes the implementing object.
+     *
+     * @param SerializerInterface $serializer
+     * @param mixed $data
+     * @param string $format
+     *
+     * @return void
+     */
+    function denormalize(SerializerInterface $serializer, $data, $format = null);
+}

+ 50 - 0
Serializer/Normalizer/NormalizableObjectNormalizer.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\Normalizer;
+
+use JMS\SerializerBundle\Serializer\InstanceCreatorInterface;
+use JMS\SerializerBundle\Exception\UnsupportedException;
+
+class NormalizableObjectNormalizer extends SerializerAwareNormalizer
+{
+    private $instanceCreator;
+
+    public function __construct(InstanceCreatorInterface $instanceCreator)
+    {
+        $this->instanceCreator = $instanceCreator;
+    }
+
+    public function normalize($data, $format = null)
+    {
+        if (!$data instanceof NormalizableInterface) {
+            throw new UnsupportedException('$data does not implement NormalizableInterface.');
+        }
+
+        return $data->normalize($this->serializer, $format);
+    }
+
+    public function denormalize($data, $type, $format = null)
+    {
+        $refl = new \ReflectionClass($type);
+        if (!$refl->isSubclassOf('JMS\SerializerBundle\Serializer\Normalizer\NormalizableInterface')) {
+            throw new UnsupportedException('$type does not implement NormalizableInterface.');
+        }
+
+        $object = $this->instanceCreator->createInstance($refl);
+        $object->denormalize($this->serializer, $data, $format);
+
+        return $object;
+    }
+
+    public function supportsNormalization($data, $format = null)
+    {
+        return $data instanceof NormalizableInterface;
+    }
+
+    public function supportsDenormalization($data, $type, $format = null)
+    {
+        $refl = new \ReflectionClass($type);
+
+        return $refl->isSubclassOf('JMS\SerializerBundle\Serializer\Normalizer\NormalizableInterface');
+    }
+}

+ 11 - 0
Serializer/Normalizer/NormalizerInterface.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\Normalizer;
+
+interface NormalizerInterface
+{
+    function normalize($data, $format = null);
+    function denormalize($data, $type, $format = null);
+    function supportsNormalization($data, $format = null);
+    function supportsDenormalization($data, $type, $format = null);
+}

+ 9 - 6
Serializer/Normalizer/PropertyBasedNormalizer.php

@@ -18,18 +18,19 @@
 
 namespace JMS\SerializerBundle\Serializer\Normalizer;
 
-use JMS\SerializerBundle\Exception\RuntimeException;
 use JMS\SerializerBundle\Annotation\Type;
 use JMS\SerializerBundle\Annotation\ExclusionPolicy;
 use JMS\SerializerBundle\Exception\InvalidArgumentException;
+use JMS\SerializerBundle\Exception\RuntimeException;
 use JMS\SerializerBundle\Exception\UnsupportedException;
 use JMS\SerializerBundle\Metadata\MetadataFactory;
 use JMS\SerializerBundle\Serializer\Exclusion\ExclusionStrategyFactoryInterface;
 use JMS\SerializerBundle\Serializer\Exclusion\ExclusionStrategyInterface;
+use JMS\SerializerBundle\Serializer\InstanceCreatorInterface;
 use JMS\SerializerBundle\Serializer\Naming\PropertyNamingStrategyInterface;
-use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
-use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
-use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
+use JMS\SerializerBundle\Serializer\Normalizer\AbstractNormalizer;
+use JMS\SerializerBundle\Serializer\Normalizer\NormalizerInterface;
+use JMS\SerializerBundle\Serializer\Normalizer\SerializerAwareNormalizer;
 
 /**
  * Generic normalizer based on class properties.
@@ -41,12 +42,14 @@ class PropertyBasedNormalizer extends SerializerAwareNormalizer
     private $metadataFactory;
     private $propertyNamingStrategy;
     private $exclusionStrategyFactory;
+    private $instanceCreator;
 
-    public function __construct(MetadataFactory $metadataFactory, PropertyNamingStrategyInterface $propertyNamingStrategy, ExclusionStrategyFactoryInterface $exclusionStrategyFactory)
+    public function __construct(MetadataFactory $metadataFactory, PropertyNamingStrategyInterface $propertyNamingStrategy, InstanceCreatorInterface $instanceCreator, ExclusionStrategyFactoryInterface $exclusionStrategyFactory)
     {
         $this->metadataFactory = $metadataFactory;
         $this->propertyNamingStrategy = $propertyNamingStrategy;
         $this->exclusionStrategyFactory = $exclusionStrategyFactory;
+        $this->instanceCreator = $instanceCreator;
     }
 
     /**
@@ -92,7 +95,7 @@ class PropertyBasedNormalizer extends SerializerAwareNormalizer
         }
 
         $metadata = $this->metadataFactory->getMetadataForClass($type);
-        $object = unserialize(sprintf('O:%d:"%s":0:{}', strlen($type), $type));
+        $object = $this->instanceCreator->createInstance($metadata->getOutsideClass()->getReflection());
 
         foreach ($metadata->getClasses() as $classMetadata) {
             $exclusionStrategy = $this->exclusionStrategyFactory->getStrategy($classMetadata->getExclusionPolicy());

+ 24 - 0
Serializer/Normalizer/SerializerAwareNormalizer.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\Normalizer;
+
+use JMS\SerializerBundle\Serializer\SerializerAwareInterface;
+use Symfony\Component\Serializer\SerializerInterface;
+
+/**
+ * SerializerAwareNormalizer base class.
+ *
+ * If you normalizer needs to work recursively, then you can extend this base
+ * class to automatically receive the serializer instance.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+abstract class SerializerAwareNormalizer implements NormalizerInterface, SerializerAwareInterface
+{
+    protected $serializer;
+
+    public function setSerializer(SerializerInterface $serializer)
+    {
+        $this->serializer = $serializer;
+    }
+}

+ 10 - 2
Serializer/Serializer.php

@@ -18,12 +18,20 @@
 
 namespace JMS\SerializerBundle\Serializer;
 
+use JMS\SerializerBundle\Serializer\Normalizer\NormalizableInterface;
+
 use JMS\SerializerBundle\Exception\RuntimeException;
-use Symfony\Component\Serializer\SerializerAwareInterface;
-use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use JMS\SerializerBundle\Serializer\SerializerAwareInterface;
+use JMS\SerializerBundle\Serializer\Normalizer\NormalizerInterface;
 use Symfony\Component\Serializer\SerializerInterface;
 
 /**
+ * Serializer implementation.
+ *
+ * This serializer distinuishes three different types of normalizers, one
+ * normalizer for native php types, one default normalizer for objects, and an
+ * arbitrary amount of specialized normalizers for specific object classes.
+ *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  */
 class Serializer implements SerializerInterface

+ 10 - 0
Serializer/SerializerAwareInterface.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer;
+
+use Symfony\Component\Serializer\SerializerInterface;
+
+interface SerializerAwareInterface
+{
+    function setSerializer(SerializerInterface $serializer);
+}

+ 13 - 0
Serializer/UnserializeInstanceCreator.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer;
+
+class UnserializeInstanceCreator implements InstanceCreatorInterface
+{
+    public function createInstance(\ReflectionClass $class)
+    {
+        $name = $class->getName();
+
+        return unserialize(sprintf('O:%d:"%s":0:{}', strlen($name), $name));
+    }
+}

+ 12 - 4
Tests/Serializer/Normalizer/PropertyBasedNormalizerTest.php

@@ -2,6 +2,11 @@
 
 namespace JMS\SerializerBundle\Tests\Serializer\Normalizer;
 
+use JMS\SerializerBundle\Serializer\Normalizer\NativePhpTypeNormalizer;
+
+use JMS\SerializerBundle\Serializer\Serializer;
+
+use JMS\SerializerBundle\Serializer\UnserializeInstanceCreator;
 use JMS\SerializerBundle\Metadata\MetadataFactory;
 use JMS\SerializerBundle\Metadata\Driver\AnnotationDriver;
 use Annotations\Reader;
@@ -11,7 +16,6 @@ use JMS\SerializerBundle\Serializer\Exclusion\DisjunctExclusionStrategy;
 use JMS\SerializerBundle\Serializer\Exclusion\VersionExclusionStrategy;
 use JMS\SerializerBundle\Serializer\Exclusion\NoneExclusionStrategy;
 use JMS\SerializerBundle\Serializer\Exclusion\AllExclusionStrategy;
-use Symfony\Component\Serializer\Serializer;
 use JMS\SerializerBundle\Tests\Fixtures\CircularReferenceParent;
 use JMS\SerializerBundle\Tests\Fixtures\SimpleObject;
 use JMS\SerializerBundle\Serializer\Exclusion\ExclusionStrategyFactory;
@@ -92,10 +96,14 @@ class PropertyBasedNormalizerTest extends \PHPUnit_Framework_TestCase
         }
         $exclusionStrategyFactory = new ExclusionStrategyFactory($strategies);
 
-        $normalizer = new PropertyBasedNormalizer(new MetadataFactory($driver), $propertyNamingStrategy, $exclusionStrategyFactory);
+        $normalizer = new PropertyBasedNormalizer(new MetadataFactory($driver), $propertyNamingStrategy, new UnserializeInstanceCreator(), $exclusionStrategyFactory);
 
-        $serializer = new Serializer();
-        $serializer->addNormalizer($normalizer);
+        $serializer = new Serializer(
+            new NativePhpTypeNormalizer(),
+            $normalizer,
+            array(),
+            array()
+        );
 
         return $normalizer;
     }

+ 3 - 1
Tests/Serializer/SerializerTest.php

@@ -2,6 +2,8 @@
 
 namespace JMS\SerializerBundle\Tests\Serializer;
 
+use JMS\SerializerBundle\Serializer\UnserializeInstanceCreator;
+
 use JMS\SerializerBundle\Metadata\Driver\AnnotationDriver;
 
 use JMS\SerializerBundle\Metadata\MetadataFactory;
@@ -102,7 +104,7 @@ class SerializerTest extends \PHPUnit_Framework_TestCase
 
         return new Serializer(
             new NativePhpTypeNormalizer(),
-            new PropertyBasedNormalizer(new MetadataFactory(new AnnotationDriver(new Reader())), $propertyNamingStrategy, $exclusionStrategyFactory),
+            new PropertyBasedNormalizer(new MetadataFactory(new AnnotationDriver(new Reader())), $propertyNamingStrategy, new UnserializeInstanceCreator(), $exclusionStrategyFactory),
             $customNormalizers,
             $encoders
         );