Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

made internals more flexible, and powerful

  • Loading branch information...
commit c263aba6a177bfd2e31f345ae67128423ea30ecc 1 parent bd1288b
@schmittjoh authored
Showing with 1,410 additions and 1,262 deletions.
  1. +24 −0 Annotation/HandlerCallback.php
  2. +65 −0 DependencyInjection/Compiler/CustomHandlersPass.php
  3. +3 −3 DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php
  4. +2 −2 DependencyInjection/Compiler/SetVisitorsPass.php
  5. +1 −16 DependencyInjection/Configuration.php
  6. +0 −32 DependencyInjection/Factory/ArrayCollectionFactory.php
  7. +0 −32 DependencyInjection/Factory/ConstraintViolationFactory.php
  8. +0 −43 DependencyInjection/Factory/DateTimeFactory.php
  9. +0 −32 DependencyInjection/Factory/DoctrineProxyFactory.php
  10. +0 −32 DependencyInjection/Factory/FormErrorFactory.php
  11. +0 −48 DependencyInjection/Factory/ObjectBasedFactory.php
  12. +1 −41 DependencyInjection/JMSSerializerExtension.php
  13. +6 −10 JMSSerializerBundle.php
  14. +9 −0 Metadata/ClassMetadata.php
  15. +10 −1 Metadata/Driver/AnnotationDriver.php
  16. +18 −2 Metadata/Driver/XmlDriver.php
  17. +13 −1 Metadata/Driver/YamlDriver.php
  18. +13 −0 Metadata/PropertyMetadata.php
  19. +27 −21 Resources/config/services.xml
  20. +20 −0 Resources/doc/reference/annotations.rst
  21. +2 −0  Resources/doc/reference/xml_reference.rst
  22. +8 −0 Resources/doc/reference/yml_reference.rst
  23. +0 −37 Serializer/AbstractDeserializationVisitor.php
  24. +0 −37 Serializer/AbstractSerializationVisitor.php
  25. +1 −3 Serializer/AbstractVisitor.php
  26. +1 −1  Serializer/Construction/DoctrineObjectConstructor.php
  27. +1 −1  Serializer/Construction/ObjectConstructorInterface.php
  28. +1 −1  Serializer/Construction/UnserializeObjectConstructor.php
  29. +8 −10 { → Serializer}/EventDispatcher/Event.php
  30. +1 −4 { → Serializer}/EventDispatcher/EventDispatcher.php
  31. +1 −1  { → Serializer}/EventDispatcher/EventDispatcherInterface.php
  32. +1 −1  { → Serializer}/EventDispatcher/EventSubscriberInterface.php
  33. +12 −0 Serializer/EventDispatcher/Events.php
  34. +1 −1  { → Serializer}/EventDispatcher/LazyEventDispatcher.php
  35. +11 −0 Serializer/EventDispatcher/PreSerializeEvent.php
  36. +30 −0 Serializer/EventDispatcher/Subscriber/DoctrineProxySubscriber.php
  37. +46 −72 Serializer/GenericDeserializationVisitor.php
  38. +10 −21 Serializer/GenericSerializationVisitor.php
  39. +114 −79 Serializer/GraphNavigator.php
  40. +44 −23 Serializer/Handler/ArrayCollectionHandler.php
  41. +70 −43 Serializer/Handler/ConstraintViolationHandler.php
  42. +53 −33 Serializer/Handler/DateTimeHandler.php
  43. +0 −26 Serializer/Handler/DeserializationHandlerInterface.php
  44. +0 −46 Serializer/Handler/DoctrineProxyHandler.php
  45. +104 −70 Serializer/Handler/FormErrorHandler.php
  46. +66 −0 Serializer/Handler/HandlerRegistry.php
  47. +39 −0 Serializer/Handler/HandlerRegistryInterface.php
  48. +41 −0 Serializer/Handler/LazyHandlerRegistry.php
  49. +0 −62 Serializer/Handler/ObjectBasedCustomHandler.php
  50. +0 −27 Serializer/Handler/SerializationHandlerInterface.php
  51. +8 −0 Serializer/Handler/SubscribingHandlerInterface.php
  52. +20 −6 Serializer/Serializer.php
  53. +165 −0 Serializer/TypeParser.php
  54. +7 −10 Serializer/VisitorInterface.php
  55. +50 −74 Serializer/XmlDeserializationVisitor.php
  56. +13 −24 Serializer/XmlSerializationVisitor.php
  57. +11 −25 Serializer/YamlSerializationVisitor.php
  58. +4 −1 Tests/DependencyInjection/JMSSerializerExtensionTest.php
  59. +6 −6 Tests/Metadata/Driver/BaseDriverTest.php
  60. +3 −3 Tests/Metadata/Driver/XmlDriverTest.php
  61. +1 −1  Tests/Metadata/Driver/YamlDriverTest.php
  62. +5 −5 Tests/Metadata/Driver/php/BlogPost.php
  63. +1 −1  Tests/Metadata/Driver/php/Price.php
  64. +65 −150 Tests/Serializer/BaseSerializationTest.php
  65. +5 −6 Tests/{ → Serializer}/EventDispatcher/EventDispatcherTest.php
  66. +53 −0 Tests/Serializer/Fixture/Article.php
  67. +15 −5 Tests/Serializer/GraphNavigatorTest.php
  68. +11 −2 Tests/Serializer/JsonSerializationTest.php
  69. +74 −0 Tests/Serializer/TypeParserTest.php
  70. +13 −25 Tests/Serializer/XmlSerializationTest.php
  71. +1 −2  Tests/Serializer/YamlSerializationTest.php
  72. +1 −0  composer.json
  73. +0 −1  phpunit.xml.dist
View
24 Annotation/HandlerCallback.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace JMS\SerializerBundle\Annotation;
+
+/**
+ * @Annotation
+ * @Target("METHOD")
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+final class HandlerCallback
+{
+ /**
+ * @Required
+ * @var string
+ */
+ public $format;
+
+ /**
+ * @Required
+ * @var string
+ */
+ public $direction;
+}
View
65 DependencyInjection/Compiler/CustomHandlersPass.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Compiler;
+
+use JMS\SerializerBundle\Serializer\Handler\HandlerRegistry;
+
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+class CustomHandlersPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ $handlers = array();
+ foreach ($container->findTaggedServiceIds('jms_serializer.handler') as $id => $tags) {
+ foreach ($tags as $attrs) {
+ if ( ! isset($attrs['type'], $attrs['format'])) {
+ throw new \RuntimeException(sprintf('Each tag named "jms_serializer.custom_handler" of service "%s" must have at least two attributes: "type", and "format".', $id));
+ }
+
+ $directions = array(GraphNavigator::DIRECTION_DESERIALIZATION, GraphNavigator::DIRECTION_SERIALIZATION);
+ if (isset($attrs['direction'])) {
+ if ( ! defined($directionConstant = 'JMS\SerializerBundle\Serializer\GraphNavigator::DIRECTION_'.strtoupper($attrs['direction']))) {
+ throw new \RuntimeException(sprintf('The direction "%s" of tag "jms_serializer.custom_handler" of service "%s" does not exist.', $attrs['direction'], $id));
+ }
+
+ $directions = array(constant($directionConstant));
+ }
+
+ foreach ($directions as $direction) {
+ $method = isset($attrs['method']) ? $attrs['method'] : HandlerRegistry::getDefaultMethod($direction, $attrs['type'], $attrs['format']);
+ $handlers[$direction][$attrs['type']][$attrs['format']] = array($id, $method);
+ }
+ }
+ }
+
+ foreach ($container->findTaggedServiceIds('jms_serializer.subscribing_handler') as $id => $tags) {
+ $class = $container->getDefinition($id)->getClass();
+ if ( ! is_subclass_of($class, 'JMS\SerializerBundle\Serializer\Handler\SubscribingHandlerInterface')) {
+ throw new \RuntimeException(sprintf('The service "%s" must implement the SubscribingHandlerInterface.', $id));
+ }
+
+ foreach (call_user_func(array($class, 'getSubscribingMethods')) as $methodData) {
+ if ( ! isset($methodData['format'], $methodData['type'])) {
+ throw new \RuntimeException(sprintf('Each method returned from getSubscribingMethods of service "%s" must have a "type", and "format" attribute.', $id));
+ }
+
+ $directions = array(GraphNavigator::DIRECTION_DESERIALIZATION, GraphNavigator::DIRECTION_SERIALIZATION);
+ if (isset($methodData['direction'])) {
+ $directions = array($methodData['direction']);
+ }
+
+ foreach ($directions as $direction) {
+ $method = isset($methodData['method']) ? $methodData['method'] : HandlerRegistry::getDefaultMethod($direction, $methodData['type'], $methodData['format']);
+ $handlers[$direction][$methodData['type']][$methodData['type']][$methodData['format']] = array($id, $method);
+ }
+ }
+ }
+
+ $container->getDefinition('jms_serializer.handler_registry')
+ ->addArgument($handlers);
+ }
+}
View
6 DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php
@@ -2,7 +2,7 @@
namespace JMS\SerializerBundle\DependencyInjection\Compiler;
-use JMS\SerializerBundle\EventDispatcher\EventDispatcher;
+use JMS\SerializerBundle\Serializer\EventDispatcher\EventDispatcher;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -28,11 +28,11 @@ public function process(ContainerBuilder $container)
foreach ($container->findTaggedServiceIds('jms_serializer.event_subscriber') as $id => $tags) {
$subscriberClass = $container->getDefinition($id)->getClass();
- if ( ! is_subclass_of($subscriberClass, 'JMS\SerializerBundle\EventDispatcher\EventSubscriberInterface')) {
+ if ( ! is_subclass_of($subscriberClass, 'JMS\SerializerBundle\Serializer\EventDispatcher\EventSubscriberInterface')) {
throw new \RuntimeException(sprintf('The service "%s" (class: %s) does not implement the EventSubscriberInterface.', $id, $subscriberClass));
}
- foreach (call_user_func($subscriberClass, 'getSubscribedEvents') as $eventData) {
+ foreach (call_user_func(array($subscriberClass, 'getSubscribedEvents')) as $eventData) {
if ( ! isset($eventData['event'])) {
throw new \RuntimeException(sprintf('The service "%s" (class: %s) must return an event for each subscribed event.', $id, $subscriberClass));
}
View
4 DependencyInjection/Compiler/SetVisitorsPass.php
@@ -61,8 +61,8 @@ public function process(ContainerBuilder $container)
foreach ($container->findTaggedServiceIds('jms_serializer.serializer') as $id => $attr) {
$container
->getDefinition($id)
- ->replaceArgument(2, $serializationVisitors)
- ->replaceArgument(3, $deserializationVisitors)
+ ->replaceArgument(5, $serializationVisitors)
+ ->replaceArgument(6, $deserializationVisitors)
;
}
}
View
17 DependencyInjection/Configuration.php
@@ -26,15 +26,13 @@
class Configuration implements ConfigurationInterface
{
private $debug;
- private $factories;
/**
* @param boolean $debug
*/
- public function __construct($debug = false, array $factories = array())
+ public function __construct($debug = false)
{
$this->debug = $debug;
- $this->factories = $factories;
}
public function getConfigTreeBuilder()
@@ -66,19 +64,6 @@ private function addSerializersSection(NodeBuilder $builder)
->end()
->end()
;
-
- $handlerNode = $builder
- ->arrayNode('handlers')
- ->addDefaultsIfNotSet()
- ->disallowNewKeysInSubsequentConfigs()
- ->children()
- ;
-
- foreach ($this->factories as $factory) {
- $factory->addConfiguration(
- $handlerNode->arrayNode($factory->getConfigKey())->canBeUnset()
- );
- }
}
private function addMetadataSection(NodeBuilder $builder)
View
32 DependencyInjection/Factory/ArrayCollectionFactory.php
@@ -1,32 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-
-class ArrayCollectionFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'array_collection';
- }
-
- public function getType(array $config)
- {
- return self::TYPE_DESERIALIZATION;
- }
-
- public function addConfiguration(ArrayNodeDefinition $builder)
- {
- $builder
- ->addDefaultsIfNotSet()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- return 'jms_serializer.array_collection_handler';
- }
-}
View
32 DependencyInjection/Factory/ConstraintViolationFactory.php
@@ -1,32 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-
-class ConstraintViolationFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'constraint_violation';
- }
-
- public function getType(array $config)
- {
- return self::TYPE_SERIALIZATION;
- }
-
- public function addConfiguration(ArrayNodeDefinition $builder)
- {
- $builder
- ->addDefaultsIfNotSet()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- return 'jms_serializer.constraint_violation_handler';
- }
-}
View
43 DependencyInjection/Factory/DateTimeFactory.php
@@ -1,43 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-class DateTimeFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'datetime';
- }
-
- public function getType(array $config)
- {
- return self::TYPE_ALL;
- }
-
- public function addConfiguration(ArrayNodeDefinition $builder)
- {
- $builder
- ->addDefaultsIfNotSet()
- ->canBeUnset()
- ->children()
- ->scalarNode('format')->defaultValue(\DateTime::ISO8601)->end()
- ->scalarNode('default_timezone')->defaultValue(date_default_timezone_get())->end()
- ->end()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- $container
- ->getDefinition('jms_serializer.datetime_handler')
- ->addArgument($config['format'])
- ->addArgument($config['default_timezone'])
- ;
-
- return 'jms_serializer.datetime_handler';
- }
-}
View
32 DependencyInjection/Factory/DoctrineProxyFactory.php
@@ -1,32 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-
-class DoctrineProxyFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'doctrine_proxy';
- }
-
- public function getType(array $config)
- {
- return self::TYPE_SERIALIZATION;
- }
-
- public function addConfiguration(ArrayNodeDefinition $builder)
- {
- $builder
- ->addDefaultsIfNotSet()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- return 'jms_serializer.doctrine_handler';
- }
-}
View
32 DependencyInjection/Factory/FormErrorFactory.php
@@ -1,32 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-
-class FormErrorFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'form_error';
- }
-
- public function getType(array $config)
- {
- return self::TYPE_SERIALIZATION;
- }
-
- public function addConfiguration(ArrayNodeDefinition $builder)
- {
- $builder
- ->addDefaultsIfNotSet()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- return 'jms_serializer.form_error_handler';
- }
-}
View
48 DependencyInjection/Factory/ObjectBasedFactory.php
@@ -1,48 +0,0 @@
-<?php
-
-namespace JMS\SerializerBundle\DependencyInjection\Factory;
-
-use JMS\SerializerBundle\DependencyInjection\HandlerFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-
-class ObjectBasedFactory implements HandlerFactoryInterface
-{
- public function getConfigKey()
- {
- return 'object_based';
- }
-
- public function getType(array $config)
- {
- $type = 0;
-
- if ($config['serialization']) {
- $type |= self::TYPE_SERIALIZATION;
- }
- if ($config['deserialization']) {
- $type |= self::TYPE_DESERIALIZATION;
- }
-
- return $type;
- }
-
- public function addConfiguration(ArrayNodeDefinition $node)
- {
- $node
- ->treatTrueLike(array('serialization' => true, 'deserialization' => true))
- ->treatNullLike(array('serialization' => true, 'deserialization' => true))
- ->treatFalseLike(array('serialization' => false, 'deserialization' => false))
- ->addDefaultsIfNotSet()
- ->children()
- ->booleanNode('serialization')->defaultFalse()->end()
- ->booleanNode('deserialization')->defaultFalse()->end()
- ->end()
- ;
- }
-
- public function getHandlerId(ContainerBuilder $container, array $config)
- {
- return 'jms_serializer.object_based_custom_handler';
- }
-}
View
42 DependencyInjection/JMSSerializerExtension.php
@@ -28,24 +28,12 @@
class JMSSerializerExtension extends ConfigurableExtension
{
- private $factories = array();
-
- public function addHandlerFactory(HandlerFactoryInterface $factory)
- {
- $this->factories[$factory->getConfigKey()] = $factory;
- }
-
public function loadInternal(array $config, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(array(
__DIR__.'/../Resources/config/')));
$loader->load('services.xml');
- // add factories as resource
- foreach ($this->factories as $factory) {
- $container->addObjectResource($factory);
- }
-
// property naming
$container
->getDefinition('jms_serializer.camel_case_naming_strategy')
@@ -60,34 +48,6 @@ public function loadInternal(array $config, ContainerBuilder $container)
$container->setAlias('jms_serializer.naming_strategy', 'jms_serializer.cache_naming_strategy');
}
- // gather handlers
- $serializationHandlers = $deserializationHandlers = array();
- foreach ($config['handlers'] as $k => $handlerConfig) {
- $id = $this->factories[$k]->getHandlerId($container, $handlerConfig);
- $type = $this->factories[$k]->getType($handlerConfig);
-
- if (0 !== ($type & HandlerFactoryInterface::TYPE_SERIALIZATION)) {
- $serializationHandlers[] = new Reference($id);
- }
-
- if (0 !== ($type & HandlerFactoryInterface::TYPE_DESERIALIZATION)) {
- $deserializationHandlers[] = new Reference($id);
- }
- }
-
- foreach (array('json', 'xml', 'yaml') as $format) {
- $container
- ->getDefinition('jms_serializer.'.$format.'_serialization_visitor')
- ->replaceArgument(1, $serializationHandlers)
- ;
- }
- foreach (array('json', 'xml') as $format) {
- $container
- ->getDefinition('jms_serializer.'.$format.'_deserialization_visitor')
- ->replaceArgument(1, $deserializationHandlers)
- ;
- }
-
// metadata
if ('none' === $config['metadata']['cache']) {
$container->removeAlias('jms_serializer.metadata.cache');
@@ -153,6 +113,6 @@ public function loadInternal(array $config, ContainerBuilder $container)
public function getConfiguration(array $config, ContainerBuilder $container)
{
- return new Configuration($container->getParameterBag()->resolveValue('%kernel.debug%'), $this->factories);
+ return new Configuration($container->getParameterBag()->resolveValue('%kernel.debug%'));
}
}
View
16 JMSSerializerBundle.php
@@ -18,6 +18,10 @@
namespace JMS\SerializerBundle;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
+
+use JMS\SerializerBundle\DependencyInjection\Compiler\SetVisitorsPass;
+use JMS\SerializerBundle\DependencyInjection\Compiler\CustomHandlersPass;
use JMS\SerializerBundle\DependencyInjection\Compiler\RegisterEventListenersAndSubscribersPass;
use JMS\SerializerBundle\DependencyInjection\Factory\FormErrorFactory;
use JMS\SerializerBundle\DependencyInjection\Factory\DateTimeFactory;
@@ -27,7 +31,6 @@
use JMS\SerializerBundle\DependencyInjection\Factory\DoctrineProxyFactory;
use JMS\SerializerBundle\DependencyInjection\JMSSerializerExtension;
use Symfony\Component\HttpKernel\KernelInterface;
-use JMS\SerializerBundle\DependencyInjection\Compiler\SetVisitorsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -36,14 +39,7 @@ class JMSSerializerBundle extends Bundle
public function build(ContainerBuilder $builder)
{
$builder->addCompilerPass(new SetVisitorsPass());
- $builder->addCompilerPass(new RegisterEventListenersAndSubscribersPass());
-
- $ext = $builder->getExtension('jms_serializer');
- $ext->addHandlerFactory(new ObjectBasedFactory());
- $ext->addHandlerFactory(new DoctrineProxyFactory());
- $ext->addHandlerFactory(new ArrayCollectionFactory());
- $ext->addHandlerFactory(new ConstraintViolationFactory());
- $ext->addHandlerFactory(new DateTimeFactory());
- $ext->addHandlerFactory(new FormErrorFactory());
+ $builder->addCompilerPass(new RegisterEventListenersAndSubscribersPass(), PassConfig::TYPE_BEFORE_REMOVING);
+ $builder->addCompilerPass(new CustomHandlersPass(), PassConfig::TYPE_BEFORE_REMOVING);
}
}
View
9 Metadata/ClassMetadata.php
@@ -41,6 +41,7 @@ class ClassMetadata extends MergeableClassMetadata
public $xmlRootName;
public $accessorOrder;
public $customOrder;
+ public $handlerCallbacks = array();
/**
* Sets the order of properties in the class.
@@ -86,6 +87,11 @@ public function addPostDeserializeMethod(MethodMetadata $method)
$this->postDeserializeMethods[] = $method;
}
+ public function addHandlerCallback($direction, $format, $methodName)
+ {
+ $this->handlerCallbacks[$direction][$format] = $methodName;
+ }
+
public function merge(MergeableInterface $object)
{
if (!$object instanceof ClassMetadata) {
@@ -98,6 +104,9 @@ public function merge(MergeableInterface $object)
$this->postDeserializeMethods = array_merge($this->postDeserializeMethods, $object->postDeserializeMethods);
$this->xmlRootName = $object->xmlRootName;
+ // Handler methods are taken from the outer class completely.
+ $this->handlerCallbacks = $object->handlerCallbacks;
+
if ($object->accessorOrder) {
$this->accessorOrder = $object->accessorOrder;
$this->customOrder = $object->customOrder;
View
11 Metadata/Driver/AnnotationDriver.php
@@ -18,6 +18,12 @@
namespace JMS\SerializerBundle\Metadata\Driver;
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+
+use JMS\SerializerBundle\Annotation\HandlerCallback;
+
+use JMS\SerializerBundle\Serializer\TypeParser;
+
use JMS\SerializerBundle\Annotation\AccessorOrder;
use JMS\SerializerBundle\Annotation\Accessor;
@@ -107,6 +113,9 @@ public function loadMetadataForClass(\ReflectionClass $class)
$propertiesMetadata[] = $virtualPropertyMetadata;
$propertiesAnnotations[] = $methodAnnotations;
continue 2;
+ } else if ($annot instanceof HandlerCallback) {
+ $classMetadata->addHandlerCallback(GraphNavigator::parseDirection($annot->direction), $annot->format, $method->name);
+ continue 2;
}
}
}
@@ -141,7 +150,7 @@ public function loadMetadataForClass(\ReflectionClass $class)
} else if ($annot instanceof Exclude) {
$isExclude = true;
} else if ($annot instanceof Type) {
- $propertyMetadata->type = $annot->name;
+ $propertyMetadata->setType($annot->name);
} else if ($annot instanceof XmlList) {
$propertyMetadata->xmlCollection = true;
$propertyMetadata->xmlCollectionInline = $annot->inline;
View
20 Metadata/Driver/XmlDriver.php
@@ -18,6 +18,8 @@
namespace JMS\SerializerBundle\Metadata\Driver;
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+
use JMS\SerializerBundle\Exception\RuntimeException;
use JMS\SerializerBundle\Exception\XmlErrorException;
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
@@ -115,9 +117,9 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
}
if (null !== $type = $pElem->attributes()->type) {
- $pMetadata->type = (string) $type;
+ $pMetadata->setType((string) $type);
} else if (isset($pElem->type)) {
- $pMetadata->type = (string) $pElem->type;
+ $pMetadata->setType((string) $pElem->type);
}
if (null !== $groups = $pElem->attributes()->groups) {
@@ -214,6 +216,20 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
$metadata->addPostDeserializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
break;
+ case 'handler':
+ if ( ! isset($method->attributes()->format)) {
+ throw new RuntimeException('The format attribute must be set for "handler" callback methods.');
+ }
+ if ( ! isset($method->attributes()->direction)) {
+ throw new RuntimeException('The direction attribute must be set for "handler" callback methods.');
+ }
+
+ $direction = GraphNavigator::parseDirection((string) $method->attributes()->direction);
+ $format = (string) $method->attributes()->format;
+ $metadata->addHandlerCallback($direction, $format, (string) $method->attributes()->name);
+
+ break;
+
default:
throw new RuntimeException(sprintf('The type "%s" is not supported.', $method->attributes()->name));
}
View
14 Metadata/Driver/YamlDriver.php
@@ -18,6 +18,8 @@
namespace JMS\SerializerBundle\Metadata\Driver;
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+
use JMS\SerializerBundle\Exception\RuntimeException;
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
use Metadata\MethodMetadata;
@@ -108,8 +110,9 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
}
if (isset($pConfig['type'])) {
- $pMetadata->type = (string) $pConfig['type'];
+ $pMetadata->setType((string) $pConfig['type']);
}
+
if (isset($pConfig['groups'])) {
$pMetadata->groups = $pConfig['groups'];
}
@@ -179,6 +182,15 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
}
}
+ if (isset($config['handler_callbacks'])) {
+ foreach ($config['handler_callbacks'] as $direction => $formats) {
+ foreach ($formats as $format => $methodName) {
+ $direction = GraphNavigator::parseDirection($direction);
+ $metadata->addHandlerCallback($direction, $format, $methodName);
+ }
+ }
+ }
+
if (isset($config['callback_methods'])) {
$cConfig = $config['callback_methods'];
View
13 Metadata/PropertyMetadata.php
@@ -18,6 +18,8 @@
namespace JMS\SerializerBundle\Metadata;
+use JMS\SerializerBundle\Serializer\TypeParser;
+
use Metadata\PropertyMetadata as BasePropertyMetadata;
class PropertyMetadata extends BasePropertyMetadata
@@ -42,6 +44,8 @@ class PropertyMetadata extends BasePropertyMetadata
public $inline = false;
public $readOnly = false;
+ private static $typeParser;
+
public function setAccessor($type, $getter = null, $setter = null)
{
if (self::ACCESS_TYPE_PUBLIC_METHOD === $type) {
@@ -70,6 +74,15 @@ public function setAccessor($type, $getter = null, $setter = null)
$this->setter = $setter;
}
+ public function setType($type)
+ {
+ if (null === self::$typeParser) {
+ self::$typeParser = new TypeParser();
+ }
+
+ $this->type = self::$typeParser->parse($type);
+ }
+
public function serialize()
{
return serialize(array(
View
48 Resources/config/services.xml
@@ -16,7 +16,7 @@
<parameter key="jms_serializer.metadata.metadata_factory.class">Metadata\MetadataFactory</parameter>
<parameter key="jms_serializer.metadata.cache.file_cache.class">Metadata\Cache\FileCache</parameter>
- <parameter key="jms_serializer.event_dispatcher.class">JMS\SerializerBundle\EventDispatcher\LazyEventDispatcher</parameter>
+ <parameter key="jms_serializer.event_dispatcher.class">JMS\SerializerBundle\Serializer\EventDispatcher\LazyEventDispatcher</parameter>
<parameter key="jms_serializer.camel_case_naming_strategy.class">JMS\SerializerBundle\Serializer\Naming\CamelCaseNamingStrategy</parameter>
<parameter key="jms_serializer.serialized_name_annotation_strategy.class">JMS\SerializerBundle\Serializer\Naming\SerializedNameAnnotationStrategy</parameter>
@@ -38,19 +38,40 @@
<parameter key="jms_serializer.xml_deserialization_visitor.class">JMS\SerializerBundle\Serializer\XmlDeserializationVisitor</parameter>
<parameter key="jms_serializer.xml_deserialization_visitor.doctype_whitelist" type="collection"></parameter>
<parameter key="jms_serializer.yaml_serialization_visitor.class">JMS\SerializerBundle\Serializer\YamlSerializationVisitor</parameter>
+
+ <parameter key="jms_serializer.handler_registry.class">JMS\SerializerBundle\Serializer\Handler\LazyHandlerRegistry</parameter>
- <parameter key="jms_serializer.object_based_custom_handler.class">JMS\SerializerBundle\Serializer\Handler\ObjectBasedCustomHandler</parameter>
<parameter key="jms_serializer.datetime_handler.class">JMS\SerializerBundle\Serializer\Handler\DateTimeHandler</parameter>
<parameter key="jms_serializer.array_collection_handler.class">JMS\SerializerBundle\Serializer\Handler\ArrayCollectionHandler</parameter>
<parameter key="jms_serializer.form_error_handler.class">JMS\SerializerBundle\Serializer\Handler\FormErrorHandler</parameter>
<parameter key="jms_serializer.constraint_violation_handler.class">JMS\SerializerBundle\Serializer\Handler\ConstraintViolationHandler</parameter>
- <parameter key="jms_serializer.doctrine_handler.class">JMS\SerializerBundle\Serializer\Handler\DoctrineProxyHandler</parameter>
+
+ <parameter key="jms_serializer.doctrine_proxy_subscriber.class">JMS\SerializerBundle\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber</parameter>
</parameters>
<services>
+ <!-- Event Dispatcher -->
<service id="jms_serializer.event_dispatcher" class="%jms_serializer.event_dispatcher.class%" public="false">
<argument type="service" id="service_container" />
</service>
+ <service id="jms_serializer.doctrine_proxy_subscriber" class="%jms_serializer.doctrine_proxy_subscriber.class%" public="false">
+ <tag name="jms_serializer.event_subscriber" />
+ </service>
+
+ <!-- Handlers -->
+ <service id="jms_serializer.handler_registry" class="%jms_serializer.handler_registry.class%" public="false">
+ <argument type="service" id="service_container" />
+ </service>
+ <service id="jms_serializer.datetime_handler" class="%jms_serializer.datetime_handler.class%" public="false">
+ <tag name="jms_serializer.subscribing_handler" />
+ </service>
+ <service id="jms_serializer.array_collection_handler" class="%jms_serializer.array_collection_handler.class%" public="false">
+ <tag name="jms_serializer.subscribing_handler" />
+ </service>
+ <service id="jms_serializer.form_error_handler" class="%jms_serializer.form_error_handler.class%" public="false">
+ <argument type="service" id="translator" />
+ </service>
+ <service id="jms_serializer.constraint_violation_handler" class="%jms_serializer.constraint_violation_handler.class%" public="false" />
<!-- Metadata Drivers -->
<service id="jms_serializer.metadata.file_locator" class="%jms_serializer.metadata.file_locator.class%" public="false">
@@ -121,7 +142,10 @@
<!-- Serializer -->
<service id="jms_serializer.serializer" class="%jms_serializer.serializer.class%" public="false">
<argument type="service" id="jms_serializer.metadata_factory" />
+ <argument type="service" id="jms_serializer.handler_registry" />
+ <argument type="service" id="jms_serializer.object_constructor" />
<argument type="service" id="jms_serializer.event_dispatcher" />
+ <argument>null</argument>
<argument type="collection" /><!-- Serialization Visitors -->
<argument type="collection" /><!-- Deserialization Visitors -->
<call method="setContainer">
@@ -140,7 +164,6 @@
<!-- Visitors -->
<service id="jms_serializer.json_serialization_visitor" class="%jms_serializer.json_serialization_visitor.class%" public="false">
<argument type="service" id="jms_serializer.naming_strategy" />
- <argument type="collection" /><!-- Custom Handlers -->
<call method="setOptions">
<argument>%jms_serializer.json_serialization_visitor.options%</argument>
</call>
@@ -148,18 +171,15 @@
</service>
<service id="jms_serializer.json_deserialization_visitor" class="%jms_serializer.json_deserialization_visitor.class%" public="false">
<argument type="service" id="jms_serializer.naming_strategy" />
- <argument type="collection" /><!-- Custom Handlers -->
<argument type="service" id="jms_serializer.object_constructor" />
<tag name="jms_serializer.deserialization_visitor" format="json" />
</service>
<service id="jms_serializer.xml_serialization_visitor" class="%jms_serializer.xml_serialization_visitor.class%" public="false">
<argument type="service" id="jms_serializer.naming_strategy" />
- <argument type="collection" /><!-- Custom Handlers -->
<tag name="jms_serializer.serialization_visitor" format="xml" />
</service>
<service id="jms_serializer.xml_deserialization_visitor" class="%jms_serializer.xml_deserialization_visitor.class%" public="false">
<argument type="service" id="jms_serializer.naming_strategy" />
- <argument type="collection" /><!-- Custom Handlers -->
<argument type="service" id="jms_serializer.object_constructor" />
<call method="setDoctypeWhitelist">
<argument>%jms_serializer.xml_deserialization_visitor.doctype_whitelist%</argument>
@@ -168,21 +188,7 @@
</service>
<service id="jms_serializer.yaml_serialization_visitor" class="%jms_serializer.yaml_serialization_visitor.class%" public="false">
<argument type="service" id="jms_serializer.naming_strategy" />
- <argument type="collection" /><!-- Custom Handlers -->
<tag name="jms_serializer.serialization_visitor" format="yml" />
</service>
-
- <!-- Custom Handlers -->
- <service id="jms_serializer.object_based_custom_handler" class="%jms_serializer.object_based_custom_handler.class%" public="false">
- <argument type="service" id="jms_serializer.unserialize_object_constructor" />
- <argument type="service" id="jms_serializer.metadata_factory" />
- </service>
- <service id="jms_serializer.datetime_handler" class="%jms_serializer.datetime_handler.class%" public="false" />
- <service id="jms_serializer.array_collection_handler" class="%jms_serializer.array_collection_handler.class%" public="false" />
- <service id="jms_serializer.form_error_handler" class="%jms_serializer.form_error_handler.class%" public="false">
- <argument type="service" id="translator" />
- </service>
- <service id="jms_serializer.constraint_violation_handler" class="%jms_serializer.constraint_violation_handler.class%" public="false" />
- <service id="jms_serializer.doctrine_handler" class="%jms_serializer.doctrine_handler.class%" public="false" />
</services>
</container>
View
20 Resources/doc/reference/annotations.rst
@@ -170,6 +170,26 @@ object has been serialized.
This annotation can be defined on a method which is supposed to be called after
the object has been deserialized.
+@HandlerCallback
+~~~~~~~~~~~~~~~~
+This annotation can be defined on a method if serialization/deserialization is handled
+by the object iself.
+
+.. code-block :: php
+
+ <?php
+
+ class Article
+ {
+ /**
+ * @HandlerCallack("xml", direction = "serialization")
+ */
+ public function serializeToXml(XmlSerializationVisitor $visitor)
+ {
+ // custom logic here
+ }
+ }
+
@Type
~~~~~
This annotation can be defined on a property to specify the type of that property.
View
2  Resources/doc/reference/xml_reference.rst
@@ -33,5 +33,7 @@ XML Reference
<callback-method name="foo" type="pre-serialize" />
<callback-method name="bar" type="post-serialize" />
<callback-method name="baz" type="post-deserialize" />
+ <callback-method name="serializeToXml" type="handler" direction="serialization" format="xml" />
+ <callback-method name="deserializeFromJson" type="handler" direction="deserialization" format="xml" />
</class>
</serializer>
View
8 Resources/doc/reference/yml_reference.rst
@@ -34,6 +34,14 @@ YAML Reference
inline: true
key_attribute_name: foo
entry_name: bar
+
+ handler_callbacks:
+ serialization:
+ xml: serializeToXml
+ json: serializeToJson
+ deserialization:
+ xml: deserializeFromXml
+
callback_methods:
pre_serialize: [foo, bar]
post_serialize: [foo, bar]
View
37 Serializer/AbstractDeserializationVisitor.php
@@ -1,37 +0,0 @@
-<?php
-
-/*
- * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace JMS\SerializerBundle\Serializer;
-
-/**
- * Abstract Deserialization Visitor.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-abstract class AbstractDeserializationVisitor extends AbstractVisitor
-{
- public function visitUsingCustomHandler($data, $type, &$visited)
- {
- foreach ($this->customHandlers as $handler) {
- $rs = $handler->deserialize($this, $data, $type, $visited);
- if ($visited) {
- return $rs;
- }
- }
- }
-}
View
37 Serializer/AbstractSerializationVisitor.php
@@ -1,37 +0,0 @@
-<?php
-
-/*
- * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace JMS\SerializerBundle\Serializer;
-
-/**
- * Abstract Serialization Visitor.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-abstract class AbstractSerializationVisitor extends AbstractVisitor
-{
- public function visitUsingCustomHandler($data, $type, &$visited)
- {
- foreach ($this->customHandlers as $handler) {
- $rs = $handler->serialize($this, $data, $type, $visited);
- if ($visited) {
- return $rs;
- }
- }
- }
-}
View
4 Serializer/AbstractVisitor.php
@@ -23,12 +23,10 @@
abstract class AbstractVisitor implements VisitorInterface
{
protected $namingStrategy;
- protected $customHandlers;
- public function __construct(PropertyNamingStrategyInterface $namingStrategy, array $customHandlers)
+ public function __construct(PropertyNamingStrategyInterface $namingStrategy)
{
$this->namingStrategy = $namingStrategy;
- $this->customHandlers = $customHandlers;
}
public function getNamingStrategy()
View
2  Serializer/Construction/DoctrineObjectConstructor.php
@@ -49,7 +49,7 @@ public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorI
/**
* {@inheritdoc}
*/
- public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, $type)
+ public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
{
// Locate possible ObjectManager
$objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
View
2  Serializer/Construction/ObjectConstructorInterface.php
@@ -23,5 +23,5 @@
interface ObjectConstructorInterface
{
- function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, $type);
+ function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type);
}
View
2  Serializer/Construction/UnserializeObjectConstructor.php
@@ -23,7 +23,7 @@
class UnserializeObjectConstructor implements ObjectConstructorInterface
{
- public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, $type)
+ public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
{
return unserialize(sprintf('O:%d:"%s":0:{}', strlen($metadata->name), $metadata->name));
}
View
18 EventDispatcher/Event.php → Serializer/EventDispatcher/Event.php
@@ -1,23 +1,21 @@
<?php
-namespace JMS\SerializerBundle\EventDispatcher;
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
use JMS\SerializerBundle\Serializer\VisitorInterface;
-
use JMS\SerializerBundle\Metadata\ClassMetadata;
class Event
{
+ protected $type;
private $object;
private $visitor;
- private $classMetadata;
- private $preventDefault = false;
- public function __construct(VisitorInterface $visitor, $object, ClassMetadata $classMetadata)
+ public function __construct(VisitorInterface $visitor, $object, array $type)
{
$this->visitor = $visitor;
$this->object = $object;
- $this->classMetadata = $classMetadata;
+ $this->type = $type;
}
public function getVisitor()
@@ -25,13 +23,13 @@ public function getVisitor()
return $this->visitor;
}
- public function getObject()
+ public function getType()
{
- return $this->object;
+ return $this->type;
}
- public function getClassMetadata()
+ public function getObject()
{
- return $this->classMetadata;
+ return $this->object;
}
}
View
5 EventDispatcher/EventDispatcher.php → ...lizer/EventDispatcher/EventDispatcher.php
@@ -1,9 +1,6 @@
<?php
-namespace JMS\SerializerBundle\EventDispatcher;
-
-use JMS\SerializerBundle\EventDispatcher\EventDispatcherInterface;
-use JMS\SerializerBundle\EventDispatcher\EventSubscriberInterface;
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
/**
* Light-weight event dispatcher.
View
2  EventDispatcher/EventDispatcherInterface.php → ...ntDispatcher/EventDispatcherInterface.php
@@ -1,6 +1,6 @@
<?php
-namespace JMS\SerializerBundle\EventDispatcher;
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
interface EventDispatcherInterface
{
View
2  EventDispatcher/EventSubscriberInterface.php → ...ntDispatcher/EventSubscriberInterface.php
@@ -1,6 +1,6 @@
<?php
-namespace JMS\SerializerBundle\EventDispatcher;
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
interface EventSubscriberInterface
{
View
12 Serializer/EventDispatcher/Events.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
+
+abstract class Events
+{
+ const PRE_SERIALIZE = 'serializer.pre_serialize';
+ const POST_SERIALIZE = 'serializer.post_serialize';
+ const POST_DESERIALIZE = 'serializer.post_deserialize';
+
+ private final function __construct() { }
+}
View
2  EventDispatcher/LazyEventDispatcher.php → ...r/EventDispatcher/LazyEventDispatcher.php
@@ -1,6 +1,6 @@
<?php
-namespace JMS\SerializerBundle\EventDispatcher;
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
use Symfony\Component\DependencyInjection\ContainerInterface;
View
11 Serializer/EventDispatcher/PreSerializeEvent.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\EventDispatcher;
+
+class PreSerializeEvent extends Event
+{
+ public function setType($typeName, array $params = array())
+ {
+ $this->type = array('name' => $typeName, 'params' => $typeName);
+ }
+}
View
30 Serializer/EventDispatcher/Subscriber/DoctrineProxySubscriber.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace JMS\SerializerBundle\Serializer\EventDispatcher\Subscriber;
+
+use Doctrine\Common\Persistence\Proxy;
+use Doctrine\ORM\Proxy\Proxy as ORMProxy;
+use JMS\SerializerBundle\Serializer\EventDispatcher\PreSerializeEvent;
+use JMS\SerializerBundle\Serializer\EventDispatcher\EventSubscriberInterface;
+
+class DoctrineProxySubscriber implements EventSubscriberInterface
+{
+ public function onPreSerialize(PreSerializeEvent $event)
+ {
+ $object = $event->getObject();
+
+ if ( ! $object instanceof Proxy && ! $object instanceof ORMProxy) {
+ return;
+ }
+
+ $object->__load();
+ $event->setType(get_parent_class($object));
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
+ );
+ }
+}
View
118 Serializer/GenericDeserializationVisitor.php
@@ -29,20 +29,13 @@
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
-abstract class GenericDeserializationVisitor extends AbstractDeserializationVisitor
+abstract class GenericDeserializationVisitor extends AbstractVisitor
{
private $navigator;
private $result;
- private $objectConstructor;
private $objectStack;
private $currentObject;
- public function __construct(PropertyNamingStrategyInterface $namingStrategy, array $customHandlers, ObjectConstructorInterface $objectConstructor)
- {
- parent::__construct($namingStrategy, $customHandlers);
- $this->objectConstructor = $objectConstructor;
- }
-
public function setNavigator(GraphNavigator $navigator)
{
$this->navigator = $navigator;
@@ -60,7 +53,7 @@ public function prepare($data)
return $this->decode($data);
}
- public function visitString($data, $type)
+ public function visitString($data, array $type)

This broke BC

@stof
stof added a note

1.0 is not BC with 0.9. There is many other BC breaks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
{
$data = (string) $data;
@@ -71,7 +64,7 @@ public function visitString($data, $type)
return $data;
}
- public function visitBoolean($data, $type)
+ public function visitBoolean($data, array $type)
{
$data = (Boolean) $data;
@@ -82,7 +75,7 @@ public function visitBoolean($data, $type)
return $data;
}
- public function visitInteger($data, $type)
+ public function visitInteger($data, array $type)
{
$data = (integer) $data;
@@ -93,7 +86,7 @@ public function visitInteger($data, $type)
return $data;
}
- public function visitDouble($data, $type)
+ public function visitDouble($data, array $type)
{
$data = (double) $data;
@@ -104,14 +97,14 @@ public function visitDouble($data, $type)
return $data;
}
- public function visitArray($data, $type)
+ public function visitArray($data, array $type)
{
- if (!is_array($data)) {
+ if ( ! is_array($data)) {
throw new RuntimeException(sprintf('Expected array, but got %s: %s', gettype($data), json_encode($data)));
}
- // not specified of which type keys/values should be, just pass as is
- if ('array' === $type) {
+ // If no further parameters were given, keys/values are just passed as is.
+ if ( ! $type['params']) {
if (null === $this->result) {
$this->result = $data;
}
@@ -119,46 +112,43 @@ public function visitArray($data, $type)
return $data;
}
- // list
- if (false === $pos = strpos($type, ',', 6)) {
- $listType = substr($type, 6, -1);
-
- $result = array();
- if (null === $this->result) {
- $this->result = &$result;
- }
-
- foreach ($data as $v) {
- $result[] = $this->navigator->accept($v, $listType, $this);
- }
-
- return $result;
- }
-
- // map
- $keyType = trim(substr($type, 6, $pos - 6));
- $entryType = trim(substr($type, $pos+1, -1));
-
- $result = array();
- if (null === $this->result) {
- $this->result = &$result;
+ switch (count($type['params'])) {
+ case 1: // Array is a list.
+ $listType = $type['params'][0];
+
+ $result = array();
+ if (null === $this->result) {
+ $this->result = &$result;
+ }
+
+ foreach ($data as $v) {
+ $result[] = $this->navigator->accept($v, $listType, $this);
+ }
+
+ return $result;
+
+ case 2: // Array is a map.
+ list($keyType, $entryType) = $type['params'];
+
+ $result = array();
+ if (null === $this->result) {
+ $this->result = &$result;
+ }
+
+ foreach ($data as $k => $v) {
+ $result[$this->navigator->accept($k, $keyType, $this)] = $this->navigator->accept($v, $entryType, $this);
+ }
+
+ return $result;
+
+ default:
+ throw new \RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
}
-
- foreach ($data as $k => $v) {
- $result[$this->navigator->accept($k, $keyType, $this)] = $this->navigator->accept($v, $entryType, $this);
- }
-
- return $result;
}
- public function visitTraversable($data, $type)
+ public function startVisitingObject(ClassMetadata $metadata, $object, array $type)
{
- throw new RuntimeException('Traversable is not supported for deserialization.');
- }
-
- public function startVisitingObject(ClassMetadata $metadata, $data, $type)
- {
- $this->setCurrentObject($this->objectConstructor->construct($this, $metadata, $data, $type));
+ $this->setCurrentObject($object);
if (null === $this->result) {
$this->result = $this->currentObject;
@@ -169,12 +159,12 @@ public function visitProperty(PropertyMetadata $metadata, $data)
{
$name = $this->namingStrategy->translateName($metadata);
- if (!isset($data[$name])) {
+ if ( ! isset($data[$name])) {
return;
}
- if (!$metadata->type) {
- throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->getDeclaringClass()->getName(), $metadata->name));
+ if ( ! $metadata->type) {
+ throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
}
$v = $this->navigator->accept($data[$name], $metadata->type, $this);
@@ -191,7 +181,7 @@ public function visitProperty(PropertyMetadata $metadata, $data)
$this->currentObject->{$metadata->setter}($v);
}
- public function endVisitingObject(ClassMetadata $metadata, $data, $type)
+ public function endVisitingObject(ClassMetadata $metadata, $data, array $type)
{
$obj = $this->currentObject;
$this->revertCurrentObject();
@@ -199,22 +189,6 @@ public function endVisitingObject(ClassMetadata $metadata, $data, $type)
return $obj;
}
- public function visitPropertyUsingCustomHandler(PropertyMetadata $metadata, $object)
- {
- // TODO
- return false;
- }
-
- public function visitUsingCustomHandler($data, $type, &$visited)
- {
- $rs = parent::visitUsingCustomHandler($data, $type, $visited);
- if (null === $this->result && $visited) {
- $this->result = $rs;
- }
- return $rs;
- }
-
-
public function getResult()
{
return $this->result;
View
31 Serializer/GenericSerializationVisitor.php
@@ -23,7 +23,7 @@
use JMS\SerializerBundle\Metadata\PropertyMetadata;
use JMS\SerializerBundle\Serializer\Naming\PropertyNamingStrategyInterface;
-abstract class GenericSerializationVisitor extends AbstractSerializationVisitor
+abstract class GenericSerializationVisitor extends AbstractVisitor
{
private $navigator;
private $root;
@@ -42,7 +42,7 @@ public function getNavigator()
return $this->navigator;
}
- public function visitString($data, $type)
+ public function visitString($data, array $type)
{
if (null === $this->root) {
$this->root = $data;
@@ -51,7 +51,7 @@ public function visitString($data, $type)
return $data;
}
- public function visitBoolean($data, $type)
+ public function visitBoolean($data, array $type)
{
if (null === $this->root) {
$this->root = $data;
@@ -60,7 +60,7 @@ public function visitBoolean($data, $type)
return $data;
}
- public function visitInteger($data, $type)
+ public function visitInteger($data, array $type)
{
if (null === $this->root) {
$this->root = $data;
@@ -69,7 +69,7 @@ public function visitInteger($data, $type)
return $data;
}
- public function visitDouble($data, $type)
+ public function visitDouble($data, array $type)
{
if (null === $this->root) {
$this->root = $data;
@@ -78,7 +78,7 @@ public function visitDouble($data, $type)
return $data;
}
- public function visitArray($data, $type)
+ public function visitArray($data, array $type)
{
if (null === $this->root) {
$this->root = array();
@@ -88,7 +88,7 @@ public function visitArray($data, $type)
}
foreach ($data as $k => $v) {
- $v = $this->navigator->accept($v, null, $this);
+ $v = $this->navigator->accept($v, isset($type['params'][1]) ? $type['params'][1] : null, $this);
if (null === $v) {
continue;
@@ -100,12 +100,7 @@ public function visitArray($data, $type)
return $rs;
}
- public function visitTraversable($data, $type)
- {
- return $this->visitArray($data, $type);
- }
-
- public function startVisitingObject(ClassMetadata $metadata, $data, $type)
+ public function startVisitingObject(ClassMetadata $metadata, $data, array $type)
{
if (null === $this->root) {
$this->root = new \stdClass;
@@ -115,7 +110,7 @@ public function startVisitingObject(ClassMetadata $metadata, $data, $type)
$this->data = array();
}
- public function endVisitingObject(ClassMetadata $metadata, $data, $type)
+ public function endVisitingObject(ClassMetadata $metadata, $data, array $type)
{
$rs = $this->data;
$this->data = $this->dataStack->pop();
@@ -132,7 +127,7 @@ public function visitProperty(PropertyMetadata $metadata, $data)
$v = (null === $metadata->getter ? $metadata->reflection->getValue($data)
: $data->{$metadata->getter}());
- $v = $this->navigator->accept($v, null, $this);
+ $v = $this->navigator->accept($v, $metadata->type, $this);
if (null === $v) {
return;
}
@@ -162,12 +157,6 @@ public function addData($key, $value)
$this->data[$key] = $value;
}
- public function visitPropertyUsingCustomHandler(PropertyMetadata $metadata, $object)
- {
- // TODO
- return false;
- }
-
public function getRoot()
{
return $this->root;
View
193 Serializer/GraphNavigator.php
@@ -18,8 +18,11 @@
namespace JMS\SerializerBundle\Serializer;
-use JMS\SerializerBundle\EventDispatcher\Event;
-use JMS\SerializerBundle\EventDispatcher\EventDispatcherInterface;
+use JMS\SerializerBundle\Serializer\EventDispatcher\PreSerializeEvent;
+use JMS\SerializerBundle\Serializer\Construction\ObjectConstructorInterface;
+use JMS\SerializerBundle\Serializer\Handler\HandlerRegistryInterface;
+use JMS\SerializerBundle\Serializer\EventDispatcher\Event;
+use JMS\SerializerBundle\Serializer\EventDispatcher\EventDispatcherInterface;
use JMS\SerializerBundle\Metadata\ClassMetadata;
use Metadata\MetadataFactoryInterface;
use JMS\SerializerBundle\Exception\InvalidArgumentException;
@@ -34,20 +37,34 @@
private $dispatcher;
private $metadataFactory;
private $format;
+ private $handlerRegistry;
+ private $objectConstructor;
private $exclusionStrategy;
+ private $customHandlers = array();
private $visiting;
- public function __construct($direction, MetadataFactoryInterface $metadataFactory, $format, ExclusionStrategyInterface $exclusionStrategy = null, EventDispatcherInterface $dispatcher = null)
+ public static function parseDirection($dirStr)
+ {
+ if ( ! defined($constant = 'JMS\SerializerBundle\Serializer\GraphNavigator::DIRECTION_'.strtoupper($dirStr))) {
+ throw new \InvalidArgumentException(sprintf('The direction "%s" does not exist.', $dirStr));
+ }
+
+ return constant($constant);
+ }
+
+ public function __construct($direction, MetadataFactoryInterface $metadataFactory, $format, HandlerRegistryInterface $handlerRegistry, ObjectConstructorInterface $objectConstructor, ExclusionStrategyInterface $exclusionStrategy = null, EventDispatcherInterface $dispatcher = null)
{
$this->direction = $direction;
$this->dispatcher = $dispatcher;
$this->metadataFactory = $metadataFactory;
$this->format = $format;
+ $this->handlerRegistry = $handlerRegistry;
+ $this->objectConstructor = $objectConstructor;
$this->exclusionStrategy = $exclusionStrategy;
$this->visiting = new \SplObjectStorage();
}
- public function accept($data, $type, VisitorInterface $visitor)
+ public function accept($data, array $type = null, VisitorInterface $visitor)
{
// determine type if not given
if (null === $type) {
@@ -55,102 +72,120 @@ public function accept($data, $type, VisitorInterface $visitor)
return null;
}
- $type = gettype($data);
- if ('object' === $type) {
- $type = get_class($data);
+ $typeName = gettype($data);
+ if ('object' === $typeName) {
+ $typeName = get_class($data);
}
+
+ $type = array('name' => $typeName, 'params' => array());
}
- if ('string' === $type) {
- return $visitor->visitString($data, $type);
- } else if ('integer' === $type) {
- return $visitor->visitInteger($data, $type);
- } else if ('boolean' === $type) {
- return $visitor->visitBoolean($data, $type);
- } else if ('double' === $type) {
- return $visitor->visitDouble($data, $type);
- } else if ('array' === $type || ('a' === $type[0] && 0 === strpos($type, 'array<'))) {
- return $visitor->visitArray($data, $type);
- } else if ('resource' === $type) {
- $msg = 'Resources are not supported in serialized data.';
- if (null !== $path = $this->getCurrentPath()) {
- $msg .= ' Path: '.implode(' -> ', $path);
- }
+ switch ($type['name']) {
+ case 'string':
+ return $visitor->visitString($data, $type);
- throw new \RuntimeException($msg);
- } else {
- if (self::DIRECTION_SERIALIZATION === $this->direction && null !== $data) {
- if ($this->visiting->contains($data)) {
- return null;
- }
- $this->visiting->attach($data);
- }
+ case 'integer':
+ return $visitor->visitInteger($data, $type);
- // try custom handler
- $handled = false;
- $rs = $visitor->visitUsingCustomHandler($data, $type, $handled);
- if ($handled) {
- if (self::DIRECTION_SERIALIZATION === $this->direction) {
- $this->visiting->detach($data);
- }
+ case 'boolean':
+ return $visitor->visitBoolean($data, $type);
- return $rs;
- }
+ case 'double':
+ return $visitor->visitDouble($data, $type);
- $metadata = $this->metadataFactory->getMetadataForClass($type);
- if (null !== $this->exclusionStrategy && $this->exclusionStrategy->shouldSkipClass($metadata, self::DIRECTION_SERIALIZATION === $this->direction ? $data : null)) {
- if (self::DIRECTION_SERIALIZATION === $this->direction) {
- $this->visiting->detach($data);
- }
+ case 'array':
+ return $visitor->visitArray($data, $type);
- return null;
- }
+ case 'resource':
+ $msg = 'Resources are not supported in serialized data.';
+ if (null !== $path = $this->getCurrentPath()) {
+ $msg .= ' Path: '.implode(' -> ', $path);
+ }
+
+ throw new \RuntimeException($msg);
- // pre-serialization callbacks
- if (self::DIRECTION_SERIALIZATION === $this->direction) {
- foreach ($metadata->preSerializeMethods as $method) {
- $method->invoke($data);
- }
+ default:
+ $isSerializing = self::DIRECTION_SERIALIZATION === $this->direction;
- if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.pre_serialize', $type, $this->format)) {
- $this->dispatcher->dispatch('serializer.pre_serialize', $type, $this->format, new Event($visitor, $data, $metadata));
+ if ($isSerializing && null !== $data) {
+ if ($this->visiting->contains($data)) {
+ return null;
+ }
+ $this->visiting->attach($data);
}
- }
- // check if traversable
- if (self::DIRECTION_SERIALIZATION === $this->direction && $data instanceof \Traversable) {
- $rs = $visitor->visitTraversable($data, $type);
- $this->afterVisitingObject($visitor, $metadata, $data, self::DIRECTION_SERIALIZATION === $this->direction);
+ // First, try whether a custom handler exists for the given type. This is done
+ // before loading metadata because the type name might not be a class, but
+ // could also simply be an artifical type.
+ if (null !== $handler = $this->handlerRegistry->getHandler($this->direction, $type['name'], $this->format)) {
+ $rs = call_user_func($handler, $visitor, $data, $type);
+
+ if ($isSerializing) {
+ $this->visiting->detach($data);
+ }
+
+ return $rs;
+ }
+
+ // Trigger pre-serialization callbacks, and listeners if they exist.
+ if ($isSerializing) {
+ if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.pre_serialize', $type['name'], $this->format)) {
+ $this->dispatcher->dispatch('serializer.pre_serialize', $type['name'], $this->format, $event = new PreSerializeEvent($visitor, $data, $type));
+ $type = $event->getType();
+ }
+ }
+
+ // Load metadata, and check whether this class should be excluded.
+ $metadata = $this->metadataFactory->getMetadataForClass($type['name']);
+ if (null !== $this->exclusionStrategy && $this->exclusionStrategy->shouldSkipClass($metadata, $isSerializing ? $data : null)) {
+ if ($isSerializing) {
+ $this->visiting->detach($data);
+ }
- return $rs;
- }
+ return null;
+ }
- $visitor->startVisitingObject($metadata, $data, $type);
- foreach ($metadata->propertyMetadata as $propertyMetadata) {
- if (null !== $this->exclusionStrategy && $this->exclusionStrategy->shouldSkipProperty($propertyMetadata, self::DIRECTION_SERIALIZATION === $this->direction ? $data : null)) {
- continue;
+ if ($isSerializing) {
+ foreach ($metadata->preSerializeMethods as $method) {
+ $method->invoke($data);
+ }
}
- if (self::DIRECTION_DESERIALIZATION === $this->direction && $propertyMetadata->readOnly) {
- continue;
+ $object = $data;
+ if ( ! $isSerializing) {
+ $object = $this->objectConstructor->construct($visitor, $metadata, $data, $type);
}
- // try custom handler
- if (!$visitor->visitPropertyUsingCustomHandler($propertyMetadata, $data)) {
+ if (isset($metadata->handlerCallbacks[$this->direction][$this->format])) {
+ $rs = $object->{$metadata->handlerCallbacks[$this->direction][$this->format]}($visitor, $isSerializing ? null : $data);
+ $this->afterVisitingObject($visitor, $metadata, $object, $type);
+
+ return $isSerializing ? $rs : $object;
+ }
+
+ $visitor->startVisitingObject($metadata, $object, $type);
+ foreach ($metadata->propertyMetadata as $propertyMetadata) {
+ if (null !== $this->exclusionStrategy && $this->exclusionStrategy->shouldSkipProperty($propertyMetadata, $isSerializing ? $data : null)) {
+ continue;
+ }
+
+ if ( ! $isSerializing && $propertyMetadata->readOnly) {
+ continue;
+ }
+
$visitor->visitProperty($propertyMetadata, $data);
}
- }
- if (self::DIRECTION_SERIALIZATION === $this->direction) {
- $this->afterVisitingObject($visitor, $metadata, $data);
+ if ($isSerializing) {
+ $this->afterVisitingObject($visitor, $metadata, $data, $type);
- return $visitor->endVisitingObject($metadata, $data, $type);
- }
+ return $visitor->endVisitingObject($metadata, $data, $type);
+ }
- $rs = $visitor->endVisitingObject($metadata, $data, $type);
- $this->afterVisitingObject($visitor, $metadata, $rs);
+ $rs = $visitor->endVisitingObject($metadata, $data, $type);
+ $this->afterVisitingObject($visitor, $metadata, $rs, $type);
- return $rs;
+ return $rs;
}
}
@@ -179,7 +214,7 @@ private function getCurrentPath()
return implode(' -> ', $path);
}
- private function afterVisitingObject(VisitorInterface $visitor, ClassMetadata $metadata, $object)
+ private function afterVisitingObject(VisitorInterface $visitor, ClassMetadata $metadata, $object, array $type)
{
if (self::DIRECTION_SERIALIZATION === $this->direction) {
$this->visiting->detach($object);
@@ -189,7 +224,7 @@ private function afterVisitingObject(VisitorInterface $visitor, ClassMetadata $m
}
if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.post_serialize', $metadata->name, $this->format)) {
- $this->dispatcher->dispatch('serializer.post_serialize', $metadata->name, $this->format, new Event($visitor, $object, $metadata));
+ $this->dispatcher->dispatch('serializer.post_serialize', $metadata->name, $this->format, new Event($visitor, $object, $type));
}
return;
@@ -200,7 +235,7 @@ private function afterVisitingObject(VisitorInterface $visitor, ClassMetadata $m
}
if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.post_deserialize', $metadata->name, $this->format)) {
- $this->dispatcher->dispatch('serializer.post_deserialize', $metadata->name, $this->format, new Event($visitor, $object, $metadata));
+ $this->dispatcher->dispatch('serializer.post_deserialize', $metadata->name, $this->format, new Event($visitor, $object, $type));
}
}
}
View
67 Serializer/Handler/ArrayCollectionHandler.php
@@ -1,37 +1,58 @@
<?php
-/*
- * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
namespace JMS\SerializerBundle\Serializer\Handler;
use Doctrine\Common\Collections\ArrayCollection;
+
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+
use JMS\SerializerBundle\Serializer\VisitorInterface;
+use Doctrine\Common\Collections\Collection;
+use JMS\SerializerBundle\Serializer\XmlSerializationVisitor;
+use JMS\SerializerBundle\Serializer\Handler\SubscribingHandlerInterface;
-class ArrayCollectionHandler implements DeserializationHandlerInterface
+class ArrayCollectionHandler implements SubscribingHandlerInterface
{
- public function deserialize(VisitorInterface $visitor, $data, $type, &$visited)
+ public static function getSubscribingMethods()