vendor/jms/serializer-bundle/DependencyInjection/Compiler/CustomHandlersPass.php line 82

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace JMS\SerializerBundle\DependencyInjection\Compiler;
  4. use JMS\Serializer\GraphNavigatorInterface;
  5. use JMS\Serializer\Handler\HandlerRegistry;
  6. use JMS\Serializer\Handler\LazyHandlerRegistry;
  7. use JMS\SerializerBundle\DependencyInjection\ScopedContainer;
  8. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  9. use Symfony\Component\DependencyInjection\Reference;
  10. /** @internal */
  11. final class CustomHandlersPass extends PerInstancePass
  12. {
  13.     protected function processInstance(ScopedContainer $container): void
  14.     {
  15.         $handlersByDirection $this->findHandlers($container);
  16.         $handlerRegistryDef $container->findDefinition('jms_serializer.handler_registry');
  17.         $isLazyHandlerRegistry is_a($handlerRegistryDef->getClass(), LazyHandlerRegistry::class, true);
  18.         $handlerServices = [];
  19.         $handlers = [];
  20.         foreach ($handlersByDirection as $direction => $handlersByType) {
  21.             foreach ($handlersByType as $type => $handlersByFormat) {
  22.                 foreach ($handlersByFormat as $format => $handlerCallable) {
  23.                     $id = (string) $handlerCallable[0];
  24.                     $handlerServices[$id] = new ServiceClosureArgument($handlerCallable[0]);
  25.                     $handlerCallable[0] = $id;
  26.                     if (!$isLazyHandlerRegistry) {
  27.                         $handlerRegistryDef->addMethodCall('registerHandler', [$direction$type$format$handlerCallable]);
  28.                     } else {
  29.                         $handlers[$direction][$type][$format] = $handlerCallable;
  30.                     }
  31.                 }
  32.             }
  33.         }
  34.         if ($isLazyHandlerRegistry) {
  35.             $handlerRegistryDef->addArgument($handlers);
  36.         }
  37.         $container->findDefinition('jms_serializer.handler_registry.service_locator')
  38.             ->setArgument(0$handlerServices);
  39.     }
  40.     private function findHandlers(ScopedContainer $container): array
  41.     {
  42.         $handlers = [];
  43.         foreach ($container->findTaggedServiceIds('jms_serializer.handler') as $id => $tags) {
  44.             foreach ($tags as $attrs) {
  45.                 if (!isset($attrs['type'], $attrs['format'])) {
  46.                     throw new \RuntimeException(sprintf('Each tag named "jms_serializer.handler" of service "%s" must have at least two attributes: "type" and "format".'$id));
  47.                 }
  48.                 $directions = [GraphNavigatorInterface::DIRECTION_DESERIALIZATIONGraphNavigatorInterface::DIRECTION_SERIALIZATION];
  49.                 if (isset($attrs['direction'])) {
  50.                     if (!defined($directionConstant 'JMS\Serializer\GraphNavigatorInterface::DIRECTION_' strtoupper($attrs['direction']))) {
  51.                         throw new \RuntimeException(sprintf('The direction "%s" of tag "jms_serializer.handler" of service "%s" does not exist.'$attrs['direction'], $id));
  52.                     }
  53.                     $directions = [constant($directionConstant)];
  54.                 }
  55.                 foreach ($directions as $direction) {
  56.                     $method $attrs['method'] ?? HandlerRegistry::getDefaultMethod($direction$attrs['type'], $attrs['format']);
  57.                     $priority = isset($attrs['priority']) ? intval($attrs['priority']) : 0;
  58.                     $handlers[] = [$direction$attrs['type'], $attrs['format'], $priority, new Reference($id), $method];
  59.                 }
  60.             }
  61.         }
  62.         foreach ($container->findTaggedServiceIds('jms_serializer.subscribing_handler') as $id => $tags) {
  63.             $def $container->getDefinition($id);
  64.             $class $def->getClass();
  65.             $ref = new \ReflectionClass($class);
  66.             if (!$ref->implementsInterface('JMS\Serializer\Handler\SubscribingHandlerInterface')) {
  67.                 throw new \RuntimeException(sprintf('The service "%s" must implement the SubscribingHandlerInterface.'$id));
  68.             }
  69.             foreach (call_user_func([$class'getSubscribingMethods']) as $methodData) {
  70.                 if (!isset($methodData['format'], $methodData['type'])) {
  71.                     throw new \RuntimeException(sprintf('Each method returned from getSubscribingMethods of service "%s" must have a "type", and "format" attribute.'$id));
  72.                 }
  73.                 $directions = [GraphNavigatorInterface::DIRECTION_DESERIALIZATIONGraphNavigatorInterface::DIRECTION_SERIALIZATION];
  74.                 if (isset($methodData['direction'])) {
  75.                     $directions = [$methodData['direction']];
  76.                 }
  77.                 foreach ($directions as $direction) {
  78.                     $priority = isset($methodData['priority']) ? intval($methodData['priority']) : 0;
  79.                     $method $methodData['method'] ?? HandlerRegistry::getDefaultMethod($direction$methodData['type'], $methodData['format']);
  80.                     $handlers[] = [$direction$methodData['type'], $methodData['format'], $priority, new Reference($id), $method];
  81.                 }
  82.             }
  83.         }
  84.         return $this->sortAndFlattenHandlersList($handlers);
  85.     }
  86.     private function sortAndFlattenHandlersList(array $allHandlers)
  87.     {
  88.         $sorter = static function ($a$b) {
  89.             return $b[3] === $a[3] ? : ($b[3] > $a[3] ? : -1);
  90.         };
  91.         self::stable_uasort($allHandlers$sorter);
  92.         $handlers = [];
  93.         foreach ($allHandlers as $handler) {
  94.             [$direction$type$format$priority$service$method] = $handler;
  95.             $handlers[$direction][$type][$format] = [$service$method];
  96.         }
  97.         return $handlers;
  98.     }
  99.     /**
  100.      * Performs stable sorting. Copied from http://php.net/manual/en/function.uasort.php#121283
  101.      *
  102.      * @param array $array
  103.      * @param callable $value_compare_func
  104.      *
  105.      * @return bool
  106.      */
  107.     private static function stable_uasort(array &$array, callable $value_compare_func)
  108.     {
  109.         $index 0;
  110.         foreach ($array as &$item) {
  111.             $item = [$index++, $item];
  112.         }
  113.         $result uasort($array, static function ($a$b) use ($value_compare_func) {
  114.             $result call_user_func($value_compare_func$a[1], $b[1]);
  115.             return === $result $a[0] - $b[0] : $result;
  116.         });
  117.         foreach ($array as &$item) {
  118.             $item $item[1];
  119.         }
  120.         return $result;
  121.     }
  122. }