minor #28907 [Messenger] make middlewares truly lazy on a bus (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[Messenger] make middlewares truly lazy on a bus
| Q | A
| ------------- | ---
| Branch? | 4.2
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
Right now, as soon as a message is dispatched on a bus, all its handlers are instantiated.
This PR fixes it by leveraging `IteratorAggregate` when possible (implemented by DI's `RewindableGenerator`).
Commits
-------
6a5d7a1aac
[Messenger] make middlewares truly lazy on a bus
This commit is contained in:
commit
5909a757c0
@ -11,28 +11,39 @@
|
||||
|
||||
namespace Symfony\Component\Messenger;
|
||||
|
||||
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
* @author Matthias Noback <matthiasnoback@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class MessageBus implements MessageBusInterface
|
||||
{
|
||||
private $middlewareHandlers;
|
||||
|
||||
/**
|
||||
* @var MiddlewareInterface[]|null
|
||||
*/
|
||||
private $indexedMiddlewareHandlers;
|
||||
private $middlewareAggregate;
|
||||
|
||||
/**
|
||||
* @param MiddlewareInterface[]|iterable $middlewareHandlers
|
||||
*/
|
||||
public function __construct(iterable $middlewareHandlers = array())
|
||||
{
|
||||
$this->middlewareHandlers = $middlewareHandlers;
|
||||
if ($middlewareHandlers instanceof \IteratorAggregate) {
|
||||
$this->middlewareAggregate = $middlewareHandlers;
|
||||
} elseif (\is_array($middlewareHandlers)) {
|
||||
$this->middlewareAggregate = new \ArrayObject($middlewareHandlers);
|
||||
} else {
|
||||
$this->middlewareAggregate = new class() {
|
||||
public $aggregate;
|
||||
public $iterator;
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return $this->aggregate = new \ArrayObject(iterator_to_array($this->iterator, false));
|
||||
}
|
||||
};
|
||||
$this->middlewareAggregate->aggregate = &$this->middlewareAggregate;
|
||||
$this->middlewareAggregate->iterator = $middlewareHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,24 +52,26 @@ class MessageBus implements MessageBusInterface
|
||||
public function dispatch($message): void
|
||||
{
|
||||
if (!\is_object($message)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid type for message argument. Expected object, but got "%s".', \gettype($message)));
|
||||
throw new \TypeError(sprintf('Invalid argument provided to "%s()": expected object, but got %s.', __METHOD__, \gettype($message)));
|
||||
}
|
||||
$middlewareIterator = $this->middlewareAggregate->getIterator();
|
||||
|
||||
$this->callableForNextMiddleware(0)($message instanceof Envelope ? $message : new Envelope($message));
|
||||
while ($middlewareIterator instanceof \IteratorAggregate) {
|
||||
$middlewareIterator = $middlewareIterator->getIterator();
|
||||
}
|
||||
$middlewareIterator->rewind();
|
||||
|
||||
private function callableForNextMiddleware(int $index): callable
|
||||
{
|
||||
if (null === $this->indexedMiddlewareHandlers) {
|
||||
$this->indexedMiddlewareHandlers = \is_array($this->middlewareHandlers) ? array_values($this->middlewareHandlers) : iterator_to_array($this->middlewareHandlers, false);
|
||||
if (!$middlewareIterator->valid()) {
|
||||
return;
|
||||
}
|
||||
$next = static function (Envelope $envelope) use ($middlewareIterator, &$next) {
|
||||
$middlewareIterator->next();
|
||||
|
||||
if (!isset($this->indexedMiddlewareHandlers[$index])) {
|
||||
return static function () {};
|
||||
if ($middlewareIterator->valid()) {
|
||||
$middlewareIterator->current()->handle($envelope, $next);
|
||||
}
|
||||
|
||||
return function (Envelope $envelope) use ($index) {
|
||||
$this->indexedMiddlewareHandlers[$index]->handle($envelope, $this->callableForNextMiddleware($index + 1));
|
||||
};
|
||||
|
||||
$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $next);
|
||||
}
|
||||
}
|
||||
|
@ -90,10 +90,10 @@ class MessengerPassTest extends TestCase
|
||||
->setAbstract(true)
|
||||
;
|
||||
|
||||
$middlewares = array(array('id' => 'call_message_handler'));
|
||||
$middlewareHandlers = array(array('id' => 'call_message_handler'));
|
||||
|
||||
$container->setParameter($commandBusId.'.middleware', $middlewares);
|
||||
$container->setParameter($queryBusId.'.middleware', $middlewares);
|
||||
$container->setParameter($commandBusId.'.middleware', $middlewareHandlers);
|
||||
$container->setParameter($queryBusId.'.middleware', $middlewareHandlers);
|
||||
|
||||
$container->register(DummyCommandHandler::class)->addTag('messenger.message_handler', array('bus' => $commandBusId));
|
||||
$container->register(DummyQueryHandler::class)->addTag('messenger.message_handler', array('bus' => $queryBusId));
|
||||
@ -607,10 +607,10 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
$container->register('console.command.messenger_debug', DebugCommand::class)->addArgument(array());
|
||||
|
||||
$middlewares = array(array('id' => 'call_message_handler'));
|
||||
$middlewareHandlers = array(array('id' => 'call_message_handler'));
|
||||
|
||||
$container->setParameter($commandBusId.'.middleware', $middlewares);
|
||||
$container->setParameter($queryBusId.'.middleware', $middlewares);
|
||||
$container->setParameter($commandBusId.'.middleware', $middlewareHandlers);
|
||||
$container->setParameter($queryBusId.'.middleware', $middlewareHandlers);
|
||||
|
||||
$container->register(DummyCommandHandler::class)->addTag('messenger.message_handler', array('bus' => $commandBusId));
|
||||
$container->register(DummyQueryHandler::class)->addTag('messenger.message_handler', array('bus' => $queryBusId));
|
||||
|
@ -30,8 +30,8 @@ class MessageBusTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Messenger\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid type for message argument. Expected object, but got "string".
|
||||
* @expectedException \TypeError
|
||||
* @expectedExceptionMessage Invalid argument provided to "Symfony\Component\Messenger\MessageBus::dispatch()": expected object, but got string.
|
||||
*/
|
||||
public function testItDispatchInvalidMessageType()
|
||||
{
|
||||
|
Reference in New Issue
Block a user