From c3ac8b4ef89196c91103374421de675eeca21632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gildas=20Qu=C3=A9m=C3=A9ner?= Date: Fri, 19 Feb 2021 14:02:52 +0100 Subject: [PATCH] [Messenger] Locate exactly one handler Related to https://github.com/symfony/symfony/issues/36958 --- .../MultipleHandlersForMessageException.php | 20 +++++ .../Handler/ExactlyOneHandlerLocator.php | 42 +++++++++++ .../Handler/ExactlyOneHandlerLocatorTest.php | 75 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/Symfony/Component/Messenger/Exception/MultipleHandlersForMessageException.php create mode 100644 src/Symfony/Component/Messenger/Handler/ExactlyOneHandlerLocator.php create mode 100644 src/Symfony/Component/Messenger/Tests/Handler/ExactlyOneHandlerLocatorTest.php diff --git a/src/Symfony/Component/Messenger/Exception/MultipleHandlersForMessageException.php b/src/Symfony/Component/Messenger/Exception/MultipleHandlersForMessageException.php new file mode 100644 index 0000000000000..f62f428e4f4c4 --- /dev/null +++ b/src/Symfony/Component/Messenger/Exception/MultipleHandlersForMessageException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Exception; + +class MultipleHandlersForMessageException extends LogicException +{ + public function __construct(string $messageType) + { + parent::__construct(sprintf('Multiple handlers for message "%s"', $messageType)); + } +} diff --git a/src/Symfony/Component/Messenger/Handler/ExactlyOneHandlerLocator.php b/src/Symfony/Component/Messenger/Handler/ExactlyOneHandlerLocator.php new file mode 100644 index 0000000000000..2fae1df95b3eb --- /dev/null +++ b/src/Symfony/Component/Messenger/Handler/ExactlyOneHandlerLocator.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Handler; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Exception\MultipleHandlersForMessageException; +use Symfony\Component\Messenger\Exception\NoHandlerForMessageException; + +final class ExactlyOneHandlerLocator implements HandlersLocatorInterface +{ + private $handlers; + + public function __construct(array $handlers) + { + foreach ($handlers as $type => $h) { + if (\count($h) > 1) { + throw new MultipleHandlersForMessageException($type); + } + } + + $this->handlers = $handlers; + } + + public function getHandlers(Envelope $envelope): iterable + { + $type = \get_class($envelope->getMessage()); + if (!isset($this->handlers[$type])) { + throw new NoHandlerForMessageException($type); + } + + yield $this->handlers[$type][0]; + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Handler/ExactlyOneHandlerLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Handler/ExactlyOneHandlerLocatorTest.php new file mode 100644 index 0000000000000..5e57cd157c015 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Handler/ExactlyOneHandlerLocatorTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Handler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Handler\HandlersLocatorInterface; +use Symfony\Component\Messenger\Handler\ExactlyOneHandlerLocator; +use Symfony\Component\Messenger\Exception\MultipleHandlersForMessageException; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\SecondMessage; +use Symfony\Component\Messenger\Exception\NoHandlerForMessageException; + +class ExactlyOneHandlerLocatorTest extends TestCase +{ + public function testItIsAHandlerLocator(): void + { + $this->assertInstanceOf(HandlersLocatorInterface::class, new ExactlyOneHandlerLocator([])); + } + + public function testItProvidesAHandler(): void + { + $handler1 = $this->createPartialMock(ExactlyOneHandlerLocatorTestCallable::class, ['__invoke']); + $handler2 = $this->createPartialMock(ExactlyOneHandlerLocatorTestCallable::class, ['__invoke']); + + $locator = new ExactlyOneHandlerLocator([ + DummyMessage::class => [$handler1], + SecondMessage::class => [$handler2], + ]); + + $this->assertSame([$handler1], iterator_to_array($locator->getHandlers( + new Envelope(new DummyMessage('Body'), [new ReceivedStamp('transportName')]) + ))); + } + + public function testItThrowsAnExceptionWhenMultipleHandlersMatchesMessage() + { + $this->expectException(MultipleHandlersForMessageException::class); + + new ExactlyOneHandlerLocator([ + DummyMessage::class => [ + $this->createPartialMock(ExactlyOneHandlerLocatorTestCallable::class, ['__invoke']), + $this->createPartialMock(ExactlyOneHandlerLocatorTestCallable::class, ['__invoke']), + ], + ]); + } + + public function testItThrowsExceptionWhenNoHandlerMatchesMessage(): void + { + $this->expectException(NoHandlerForMessageException::class); + + $locator = new ExactlyOneHandlerLocator([]); + iterator_to_array($locator->getHandlers( + new Envelope(new DummyMessage('Body'), [new ReceivedStamp('transportName')]) + )); + } +} + +class ExactlyOneHandlerLocatorTestCallable +{ + public function __invoke() + { + } +}