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

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