diff --git a/src/Symfony/Component/EventDispatcher/Async/AsyncEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Async/AsyncEventDispatcher.php new file mode 100644 index 0000000000000..d12dcfff4c635 --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/AsyncEventDispatcher.php @@ -0,0 +1,57 @@ +trueEventDispatcher = $trueEventDispatcher; + $this->asyncListener = $asyncListener; + } + + /** + * This method dispatches only those listeners that were marked as async. + * + * @param string $eventName + * @param Event|null $event + */ + public function dispatchAsyncListenersOnly($eventName, Event $event = null) + { + try { + $this->asyncListener->syncMode($eventName); + + parent::dispatch($eventName, $event); + } finally { + $this->asyncListener->resetSyncMode(); + } + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + parent::dispatch($eventName, $event); + + $this->trueEventDispatcher->dispatch($eventName, $event); + } +} diff --git a/src/Symfony/Component/EventDispatcher/Async/AsyncListener.php b/src/Symfony/Component/EventDispatcher/Async/AsyncListener.php new file mode 100644 index 0000000000000..58111ae4e7fc6 --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/AsyncListener.php @@ -0,0 +1,87 @@ +context = $context; + $this->registry = $registry; + $this->eventQueue = $eventQueue instanceof PsrQueue ? $eventQueue : $context->createQueue($eventQueue); + } + + public function __invoke(Event $event, $eventName) + { + $this->onEvent($event, $eventName); + } + + public function resetSyncMode() + { + $this->syncMode = []; + } + + /** + * @param string $eventName + */ + public function syncMode($eventName) + { + $this->syncMode[$eventName] = true; + } + + /** + * @param string $eventName + * + * @return bool + */ + public function isSyncMode($eventName) + { + return isset($this->syncMode[$eventName]); + } + + /** + * @param Event $event + * @param string $eventName + */ + public function onEvent(Event $event, $eventName) + { + if (false == isset($this->syncMode[$eventName])) { + $transformerName = $this->registry->getTransformerNameForEvent($eventName); + + $message = $this->registry->getTransformer($transformerName)->toMessage($eventName, $event); + $message->setProperty('event_name', $eventName); + $message->setProperty('transformer_name', $transformerName); + + $this->context->createProducer()->send($this->eventQueue, $message); + } + } +} diff --git a/src/Symfony/Component/EventDispatcher/Async/AsyncProcessor.php b/src/Symfony/Component/EventDispatcher/Async/AsyncProcessor.php new file mode 100644 index 0000000000000..f6ef68b8b2655 --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/AsyncProcessor.php @@ -0,0 +1,50 @@ +registry = $registry; + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function process(PsrMessage $message, PsrContext $context) + { + if (false == $eventName = $message->getProperty('event_name')) { + return self::REJECT; + } + if (false == $transformerName = $message->getProperty('transformer_name')) { + return self::REJECT; + } + + $event = $this->registry->getTransformer($transformerName)->toEvent($eventName, $message); + + $this->dispatcher->dispatchAsyncListenersOnly($eventName, $event); + + return self::ACK; + } +} diff --git a/src/Symfony/Component/EventDispatcher/Async/ContainerAwareRegistry.php b/src/Symfony/Component/EventDispatcher/Async/ContainerAwareRegistry.php new file mode 100644 index 0000000000000..fbc6a439d346c --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/ContainerAwareRegistry.php @@ -0,0 +1,82 @@ + transformerName] + * @param string[] $transformersMap [transformerName => transformerServiceId] + */ + public function __construct(array $eventsMap, array $transformersMap) + { + $this->eventsMap = $eventsMap; + $this->transformersMap = $transformersMap; + } + + /** + * {@inheritdoc} + */ + public function getTransformerNameForEvent($eventName) + { + $transformerName = null; + if (array_key_exists($eventName, $this->eventsMap)) { + $transformerName = $this->eventsMap[$eventName]; + } else { + foreach ($this->eventsMap as $eventNamePattern => $name) { + if ('/' != $eventNamePattern[0]) { + continue; + } + + if (preg_match($eventNamePattern, $eventName)) { + $transformerName = $name; + + break; + } + } + } + + if (empty($transformerName)) { + throw new \LogicException(sprintf('There is no transformer registered for the given event %s', $eventName)); + } + + return $transformerName; + } + + /** + * {@inheritdoc} + */ + public function getTransformer($name) + { + if (false == array_key_exists($name, $this->transformersMap)) { + throw new \LogicException(sprintf('There is no transformer named %s', $name)); + } + + $transformer = $this->container->get($this->transformersMap[$name]); + + if (false == $transformer instanceof EventTransformer) { + throw new \LogicException(sprintf( + 'The container must return instance of %s but got %s', + EventTransformer::class, + is_object($transformer) ? get_class($transformer) : gettype($transformer) + )); + } + + return $transformer; + } +} diff --git a/src/Symfony/Component/EventDispatcher/Async/EventTransformer.php b/src/Symfony/Component/EventDispatcher/Async/EventTransformer.php new file mode 100644 index 0000000000000..c0f19a535d27a --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/EventTransformer.php @@ -0,0 +1,29 @@ +context = $context; + } + + /** + * {@inheritdoc} + */ + public function toMessage($eventName, Event $event = null) + { + return $this->context->createMessage(serialize($event)); + } + + /** + * {@inheritdoc} + */ + public function toEvent($eventName, PsrMessage $message) + { + return unserialize($message->getBody()); + } +} diff --git a/src/Symfony/Component/EventDispatcher/Async/Registry.php b/src/Symfony/Component/EventDispatcher/Async/Registry.php new file mode 100644 index 0000000000000..920e2ce92c35d --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Async/Registry.php @@ -0,0 +1,20 @@ + transformerName] + * @param string[] $transformersMap [transformerName => transformerObject] + */ + public function __construct(array $eventsMap, array $transformersMap) + { + $this->eventsMap = $eventsMap; + $this->transformersMap = $transformersMap; + } + + /** + * {@inheritdoc} + */ + public function getTransformerNameForEvent($eventName) + { + $transformerName = null; + if (array_key_exists($eventName, $this->eventsMap)) { + $transformerName = $this->eventsMap[$eventName]; + } else { + foreach ($this->eventsMap as $eventNamePattern => $name) { + if ('/' != $eventNamePattern[0]) { + continue; + } + + if (preg_match($eventNamePattern, $eventName)) { + $transformerName = $name; + + break; + } + } + } + + if (empty($transformerName)) { + throw new \LogicException(sprintf('There is no transformer registered for the given event %s', $eventName)); + } + + return $transformerName; + } + + /** + * {@inheritdoc} + */ + public function getTransformer($name) + { + if (false == array_key_exists($name, $this->transformersMap)) { + throw new \LogicException(sprintf('There is no transformer named %s', $name)); + } + + $transformer = $this->transformersMap[$name]; + + if (false == $transformer instanceof EventTransformer) { + throw new \LogicException(sprintf( + 'The container must return instance of %s but got %s', + EventTransformer::class, + is_object($transformer) ? get_class($transformer) : gettype($transformer) + )); + } + + return $transformer; + } +} diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index cc79a3141d42d..e49e243467b14 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=5.5.9", + "queue-interop/queue-interop": "^0.5" }, "require-dev": { "symfony/dependency-injection": "~3.3|~4.0",