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

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