Skip to content

Commit

Permalink
minor #28907 [Messenger] make middlewares truly lazy on a bus (nicola…
Browse files Browse the repository at this point in the history
…s-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
-------

6a5d7a1 [Messenger] make middlewares truly lazy on a bus
  • Loading branch information
sroze committed Oct 21, 2018
2 parents f24794d + 6a5d7a1 commit 5909a75
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 28 deletions.
53 changes: 33 additions & 20 deletions src/Symfony/Component/Messenger/MessageBus.php
Expand Up @@ -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;
}
}

/**
Expand All @@ -41,24 +52,26 @@ public function __construct(iterable $middlewareHandlers = array())
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));
}

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);
while ($middlewareIterator instanceof \IteratorAggregate) {
$middlewareIterator = $middlewareIterator->getIterator();
}
$middlewareIterator->rewind();

if (!isset($this->indexedMiddlewareHandlers[$index])) {
return static function () {};
if (!$middlewareIterator->valid()) {
return;
}
$next = static function (Envelope $envelope) use ($middlewareIterator, &$next) {
$middlewareIterator->next();

return function (Envelope $envelope) use ($index) {
$this->indexedMiddlewareHandlers[$index]->handle($envelope, $this->callableForNextMiddleware($index + 1));
if ($middlewareIterator->valid()) {
$middlewareIterator->current()->handle($envelope, $next);
}
};

$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $next);
}
}
Expand Up @@ -90,10 +90,10 @@ public function testProcessHandlersByBus()
->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));
Expand Down Expand Up @@ -607,10 +607,10 @@ public function testItRegistersTheDebugCommand()

$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));
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Messenger/Tests/MessageBusTest.php
Expand Up @@ -30,8 +30,8 @@ public function testItHasTheRightInterface()
}

/**
* @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()
{
Expand Down

0 comments on commit 5909a75

Please sign in to comment.