vendor/jms/serializer/src/EventDispatcher/Subscriber/DoctrineProxySubscriber.php line 101

  1. <?php
  2. declare(strict_types=1);
  3. namespace JMS\Serializer\EventDispatcher\Subscriber;
  4. use Doctrine\Common\Persistence\Proxy as LegacyProxy;
  5. use Doctrine\ODM\MongoDB\PersistentCollection as MongoDBPersistentCollection;
  6. use Doctrine\ODM\PHPCR\PersistentCollection as PHPCRPersistentCollection;
  7. use Doctrine\ORM\PersistentCollection;
  8. use Doctrine\Persistence\Proxy;
  9. use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
  10. use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
  11. use JMS\Serializer\EventDispatcher\PreSerializeEvent;
  12. use ProxyManager\Proxy\LazyLoadingInterface;
  13. final class DoctrineProxySubscriber implements EventSubscriberInterface
  14. {
  15. /**
  16. * @var bool
  17. */
  18. private $skipVirtualTypeInit;
  19. /**
  20. * @var bool
  21. */
  22. private $initializeExcluded;
  23. public function __construct(bool $skipVirtualTypeInit = true, bool $initializeExcluded = false)
  24. {
  25. $this->skipVirtualTypeInit = $skipVirtualTypeInit;
  26. $this->initializeExcluded = $initializeExcluded;
  27. }
  28. public function onPreSerialize(PreSerializeEvent $event): void
  29. {
  30. $object = $event->getObject();
  31. $type = $event->getType();
  32. // If the set type name is not an actual class, but a faked type for which a custom handler exists, we do not
  33. // modify it with this subscriber. Also, we forgo autoloading here as an instance of this type is already created,
  34. // so it must be loaded if its a real class.
  35. $virtualType = !class_exists($type['name'], false);
  36. if (
  37. $object instanceof PersistentCollection
  38. || $object instanceof MongoDBPersistentCollection
  39. || $object instanceof PHPCRPersistentCollection
  40. ) {
  41. if (!$virtualType) {
  42. $event->setType('ArrayCollection');
  43. }
  44. return;
  45. }
  46. if (
  47. ($this->skipVirtualTypeInit && $virtualType) ||
  48. (!$object instanceof Proxy && !$object instanceof LazyLoadingInterface)
  49. ) {
  50. return;
  51. }
  52. // do not initialize the proxy if is going to be excluded by-class by some exclusion strategy
  53. if (false === $this->initializeExcluded && !$virtualType) {
  54. $context = $event->getContext();
  55. $exclusionStrategy = $context->getExclusionStrategy();
  56. $metadata = $context->getMetadataFactory()->getMetadataForClass(get_parent_class($object));
  57. if (null !== $metadata && null !== $exclusionStrategy && $exclusionStrategy->shouldSkipClass($metadata, $context)) {
  58. return;
  59. }
  60. }
  61. if ($object instanceof LazyLoadingInterface) {
  62. $object->initializeProxy();
  63. } else {
  64. $object->__load();
  65. }
  66. if (!$virtualType) {
  67. $event->setType(get_parent_class($object), $type['params']);
  68. }
  69. }
  70. public function onPreSerializeTypedProxy(PreSerializeEvent $event, string $eventName, string $class, string $format, EventDispatcherInterface $dispatcher): void
  71. {
  72. $type = $event->getType();
  73. // is a virtual type? then there is no need to change the event name
  74. if (!class_exists($type['name'], false)) {
  75. return;
  76. }
  77. $object = $event->getObject();
  78. if ($object instanceof Proxy) {
  79. $parentClassName = get_parent_class($object);
  80. // check if this is already a re-dispatch
  81. if (strtolower($class) !== strtolower($parentClassName)) {
  82. $event->stopPropagation();
  83. $newEvent = new PreSerializeEvent($event->getContext(), $object, ['name' => $parentClassName, 'params' => $type['params']]);
  84. $dispatcher->dispatch($eventName, $parentClassName, $format, $newEvent);
  85. // update the type in case some listener changed it
  86. $newType = $newEvent->getType();
  87. $event->setType($newType['name'], $newType['params']);
  88. }
  89. }
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public static function getSubscribedEvents()
  95. {
  96. return [
  97. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerializeTypedProxy', 'interface' => Proxy::class],
  98. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerializeTypedProxy', 'interface' => LegacyProxy::class],
  99. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => PersistentCollection::class],
  100. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => MongoDBPersistentCollection::class],
  101. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => PHPCRPersistentCollection::class],
  102. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => Proxy::class],
  103. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => LegacyProxy::class],
  104. ['event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize', 'interface' => LazyLoadingInterface::class],
  105. ];
  106. }
  107. }