From 2d6a6a045aa425d2af3900bcce85e66152e1dece Mon Sep 17 00:00:00 2001 From: Max Kachimov Date: Thu, 20 Feb 2025 01:46:49 -0800 Subject: [PATCH] Add global handlers for all data types --- doc/handlers.rst | 47 +++++++++++++++++++ src/Handler/HandlerRegistry.php | 12 +++-- src/Handler/LazyHandlerRegistry.php | 15 ++++-- tests/Handler/HandlerRegistryTest.php | 20 ++++++++ tests/Handler/LazyHandlerRegistryTestCase.php | 24 ++++++++++ 5 files changed, 112 insertions(+), 6 deletions(-) diff --git a/doc/handlers.rst b/doc/handlers.rst index a7c1b717c..edaff566b 100644 --- a/doc/handlers.rst +++ b/doc/handlers.rst @@ -83,3 +83,50 @@ Skippable Subscribing Handlers In case you need to be able to fall back to the default deserialization behavior instead of using your custom handler, you can simply throw a `SkipHandlerException` from you custom handler method to do so. + +Global Subscribing Handlers +---------------- +You can register global handler, if type is '*':: + + use JMS\Serializer\Handler\SubscribingHandlerInterface; + use JMS\Serializer\GraphNavigator; + use JMS\Serializer\JsonSerializationVisitor; + use JMS\Serializer\JsonDeserializationVisitor; + use JMS\Serializer\Context; + + class MyHandler implements SubscribingHandlerInterface + { + public static function getSubscribingMethods() + { + return [ + [ + 'direction' => GraphNavigator::DIRECTION_SERIALIZATION, + 'format' => 'json', + 'type' => '*', + 'method' => 'handleCustomSerialization', + ], + [ + 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION, + 'format' => 'json', + 'type' => '*', + 'method' => 'handleCustomDeserialization', + ], + ]; + } + + public function handleCustomSerialization(JsonSerializationVisitor $visitor, mixed $data, array $type, Context $context) + { + // preform custom logic + } + + public function handleCustomDeserialization(JsonDeserializationVisitor $visitor, mixed $data, array $type, Context $context) + { + // preform custom logic + } + } +.. + + Be aware that when you register a global handler, + it will be triggered for every serialization/deserialization process, + unless another custom handler specified. + Be sure to properly handle data processing. \ No newline at end of file diff --git a/src/Handler/HandlerRegistry.php b/src/Handler/HandlerRegistry.php index 1b8ae94ae..bf031b0ae 100644 --- a/src/Handler/HandlerRegistry.php +++ b/src/Handler/HandlerRegistry.php @@ -10,6 +10,8 @@ class HandlerRegistry implements HandlerRegistryInterface { + protected const GLOBAL_HANDLER_TYPE = '*'; + /** * @var callable[] */ @@ -70,11 +72,15 @@ public function registerHandler(int $direction, string $typeName, string $format */ public function getHandler(int $direction, string $typeName, string $format) { - if (!isset($this->handlers[$direction][$typeName][$format])) { - return null; + if (isset($this->handlers[$direction][$typeName][$format])) { + return $this->handlers[$direction][$typeName][$format]; + } + + if (isset($this->handlers[$direction][self::GLOBAL_HANDLER_TYPE][$format])) { + return $this->handlers[$direction][self::GLOBAL_HANDLER_TYPE][$format]; } - return $this->handlers[$direction][$typeName][$format]; + return null; } /** diff --git a/src/Handler/LazyHandlerRegistry.php b/src/Handler/LazyHandlerRegistry.php index 7914aeaf3..da03061d4 100644 --- a/src/Handler/LazyHandlerRegistry.php +++ b/src/Handler/LazyHandlerRegistry.php @@ -54,11 +54,20 @@ public function getHandler(int $direction, string $typeName, string $format) return $this->initializedHandlers[$direction][$typeName][$format]; } - if (!isset($this->handlers[$direction][$typeName][$format])) { - return null; + $handler = $this->handlers[$direction][$typeName][$format] ?? null; + + if (null === $handler) { + if (isset($this->initializedHandlers[$direction][self::GLOBAL_HANDLER_TYPE][$format])) { + return $this->initializedHandlers[$direction][self::GLOBAL_HANDLER_TYPE][$format]; + } + + if (null === $handler = $this->handlers[$direction][self::GLOBAL_HANDLER_TYPE][$format]) { + return null; + } + + $typeName = self::GLOBAL_HANDLER_TYPE; } - $handler = $this->handlers[$direction][$typeName][$format]; if (\is_array($handler) && \is_string($handler[0]) && $this->container->has($handler[0])) { $handler[0] = $this->container->get($handler[0]); } diff --git a/tests/Handler/HandlerRegistryTest.php b/tests/Handler/HandlerRegistryTest.php index 91f9b30df..833760c66 100644 --- a/tests/Handler/HandlerRegistryTest.php +++ b/tests/Handler/HandlerRegistryTest.php @@ -37,6 +37,26 @@ public function testRegisteredHandlersCanBeRetrieved() self::assertSame($xmlDeserializationHandler, $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '\stdClass', 'xml')); } + public function testRegisteredGlobalHandlersCanBeRetrieved() + { + $jsonSerializationHandler = new DummyHandler(); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'json', $jsonSerializationHandler); + + $jsonDeserializationHandler = new DummyHandler(); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'json', $jsonDeserializationHandler); + + $xmlSerializationHandler = new DummyHandler(); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'xml', $xmlSerializationHandler); + + $xmlDeserializationHandler = new DummyHandler(); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'xml', $xmlDeserializationHandler); + + self::assertSame($jsonSerializationHandler, $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'json')); + self::assertSame($jsonDeserializationHandler, $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'json')); + self::assertSame($xmlSerializationHandler, $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'xml')); + self::assertSame($xmlDeserializationHandler, $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'xml')); + } + protected function createHandlerRegistry() { return new HandlerRegistry(); diff --git a/tests/Handler/LazyHandlerRegistryTestCase.php b/tests/Handler/LazyHandlerRegistryTestCase.php index 57d0b34f2..a1a3b11ce 100644 --- a/tests/Handler/LazyHandlerRegistryTestCase.php +++ b/tests/Handler/LazyHandlerRegistryTestCase.php @@ -47,6 +47,30 @@ public function testRegisteredHandlersCanBeRetrievedWhenBeingDefinedAsServices() self::assertSame([$xmlDeserializationHandler, 'handle'], $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '\stdClass', 'xml')); } + public function testRegisteredGlobalHandlersCanBeRetrievedWhenBeingDefinedAsServices() + { + $jsonSerializationHandler = new HandlerService(); + $this->registerHandlerService('handler.serialization.json', $jsonSerializationHandler); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'json', ['handler.serialization.json', 'handle']); + + $jsonDeserializationHandler = new HandlerService(); + $this->registerHandlerService('handler.deserialization.json', $jsonDeserializationHandler); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'json', ['handler.deserialization.json', 'handle']); + + $xmlSerializationHandler = new HandlerService(); + $this->registerHandlerService('handler.serialization.xml', $xmlSerializationHandler); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'xml', ['handler.serialization.xml', 'handle']); + + $xmlDeserializationHandler = new HandlerService(); + $this->registerHandlerService('handler.deserialization.xml', $xmlDeserializationHandler); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'xml', ['handler.deserialization.xml', 'handle']); + + self::assertSame([$jsonSerializationHandler, 'handle'], $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'json')); + self::assertSame([$jsonDeserializationHandler, 'handle'], $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'json')); + self::assertSame([$xmlSerializationHandler, 'handle'], $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, '*', 'xml')); + self::assertSame([$xmlDeserializationHandler, 'handle'], $this->handlerRegistry->getHandler(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, '*', 'xml')); + } + abstract protected function createContainer(); abstract protected function registerHandlerService($serviceId, $listener);