8000 [Messenger] Add `StackInterface`, allowing to unstack the call stack by nicolas-grekas · Pull Request #28943 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Messenger] Add StackInterface, allowing to unstack the call stack #28943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;

/**
* Wraps all handlers in a single doctrine transaction.
Expand All @@ -35,7 +36,7 @@ public function __construct(ManagerRegistry $managerRegistry, ?string $entityMan
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
$entityManager = $this->managerRegistry->getManager($this->entityManagerName);

Expand All @@ -45,7 +46,7 @@ public function handle(Envelope $envelope, callable $next): void

$entityManager->getConnection()->beginTransaction();
try {
$next($envelope);
$stack->next()->handle($envelope, $stack);
$entityManager->flush();
$entityManager->getConnection()->commit();
} catch (\Throwable $exception) {
Expand Down
3 changes: 1 addition & 2 deletions src/Symfony/Component/Messenger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CHANGELOG
* The component is not experimental anymore
* All the changes below are BC BREAKS
* `MessageBusInterface::dispatch()` and `MiddlewareInterface::handle()` now return `void`
* `MiddlewareInterface::handle()` now require an `Envelope` as first argument
* `MiddlewareInterface::handle()` now require an `Envelope` as first argument and a `StackInterface` as second
* `EnvelopeAwareInterface` has been removed
* The signature of `Amqp*` classes changed to take a `Connection` as a first argument and an optional
`Serializer` as a second argument.
Expand All @@ -16,7 +16,6 @@ CHANGELOG
Instead, it accepts the sender instance itself instead of its identifier in the container.
* `MessageSubscriberInterface::getHandledMessages()` return value has changed. The value of an array item
needs to be an associative array or the method name.
* `ValidationMiddleware::handle()` and `SendMessageMiddleware::handle()` now require an `Envelope` object
* `StampInterface` replaces `EnvelopeItemInterface` and doesn't extend `Serializable` anymore
* The `ConsumeMessagesCommand` class now takes an instance of `Psr\Container\ContainerInterface`
as first constructor argument
Expand Down
11 changes: 3 additions & 8 deletions src/Symfony/Component/Messenger/MessageBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger;

use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackMiddleware;

/**
* @author Samuel Roze <samuel.roze@gmail.com>
Expand Down Expand Up @@ -64,14 +65,8 @@ public function dispatch($message): void
if (!$middlewareIterator->valid()) {
return;
}
$next = static function (Envelope $envelope) use ($middlewareIterator, &$next) {
$middlewareIterator->next();
$stack = new StackMiddleware($middlewareIterator);

if ($middlewareIterator->valid()) {
$middlewareIterator->current()->handle($envelope, $next);
}
};

$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $next);
$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $stack);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public function __construct(MiddlewareInterface $inner, $activated)
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
if (\is_callable($this->activated) ? ($this->activated)($envelope) : $this->activated) {
$this->inner->handle($envelope, $next);
$this->inner->handle($envelope, $stack);
} else {
$next($envelope);
$stack->next()->handle($envelope, $stack);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public function __construct(HandlerLocatorInterface $messageHandlerLocator, bool
*
* @throws NoHandlerForMessageException When no handler is found and $allowNoHandlers is false
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
if (null !== $handler = $this->messageHandlerLocator->getHandler($envelope)) {
$handler($envelope->getMessage());
$next($envelope);
$stack->next()->handle($envelope, $stack);
} elseif (!$this->allowNoHandlers) {
throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', \get_class($envelope->getMessage())));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function __construct(LoggerInterface $logger)
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
$message = $envelope->getMessage();
$context = array(
Expand All @@ -39,7 +39,7 @@ public function handle(Envelope $envelope, callable $next): void
$this->logger->debug('Starting handling message {name}', $context);

try {
$next($envelope);
$stack->next()->handle($envelope, $stack);
} catch (\Throwable $e) {
$context['exception'] = $e;
$this->logger->warning('An exception occurred while handling message {name}', $context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,5 @@
*/
interface MiddlewareInterface
{
/**
* @param callable|NextInterface $next
*/
public function handle(Envelope $envelope, callable $next): void;
}

/**
* @internal
*/
interface NextInterface
{
public function __invoke(Envelope $envelope): void;
public function handle(Envelope $envelope, StackInterface $stack): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public function __construct(SenderLocatorInterface $senderLocator, array $messag
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
if ($envelope->get(ReceivedStamp::class)) {
// It's a received message. Do not send it back:
$next($envelope);
$stack->next()->handle($envelope, $stack);

return;
}
Expand All @@ -54,6 +54,6 @@ public function handle(Envelope $envelope, callable $next): void
}
}

$next($envelope);
$stack->next()->handle($envelope, $stack);
}
}
23 changes: 23 additions & 0 deletions src/Symfony/Component/Messenger/Middleware/StackInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Messenger\Middleware;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
interface StackInterface
{
/**
* Returns the next middleware to process a message.
*/
public function next(): MiddlewareInterface;
}
48 changes: 48 additions & 0 deletions src/Symfony/Component/Messenger/Middleware/StackMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Messenger\Middleware;

use Symfony\Component\Messenger\Envelope;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class StackMiddleware implements MiddlewareInterface, StackInterface
{
private $middlewareIterator;

public function __construct(\Iterator $middlewareIterator = null)
{
$this->middlewareIterator = $middlewareIterator;
}

public function next(): MiddlewareInterface
{
if (null === $iterator = $this->middlewareIterator) {
return $this;
}
$iterator->next();

if (!$iterator->valid()) {
$this->middlewareIterator = null;

return $this;
}

return $iterator->current();
}

public function handle(Envelope $envelope, StackInterface $stack): void
{
// no-op: this is the last null middleware
}
}
49 changes: 43 additions & 6 deletions src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function __construct(MiddlewareInterface $inner, Stopwatch $stopwatch, st
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
$class = \get_class($this->inner);
$eventName = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
Expand All @@ -49,15 +49,52 @@ public function handle(Envelope $envelope, callable $next): void
$this->stopwatch->start($eventName, $this->eventCategory);

try {
$this->inner->handle($envelope, function (Envelope $envelope) use ($next, $eventName) {
$this->stopwatch->stop($eventName);
$next($envelope);
$this->stopwatch->start($eventName, $this->eventCategory);
});
$this->inner->handle($envelope, new TraceableInnerMiddleware($stack, $this->stopwatch, $eventName, $this->eventCategory));
} finally {
if ($this->stopwatch->isStarted($eventName)) {
$this->stopwatch->stop($eventName);
}
}
}
}

/**
* @internal
*/
class TraceableInnerMiddleware implements MiddlewareInterface, StackInterface
{
private $stack;
private $stopwatch;
private $eventName;
private $eventCategory;

public function __construct(StackInterface $stack, Stopwatch $stopwatch, string $eventName, string $eventCategory)
{
$this->stack = $stack;
$this->stopwatch = $stopwatch;
$this->eventName = $eventName;
$this->eventCategory = $eventCategory;
}

/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, StackInterface $stack): void
{
$this->stopwatch->stop($this->eventName);
if ($this === $stack) {
$this->stack->next()->handle($envelope, $this->stack);
} else {
$stack->next()->handle($envelope, $stack);
}
$this->stopwatch->start($this->eventName, $this->eventCategory);
}

/**
* {@inheritdoc}
*/
public function next(): MiddlewareInterface
{
return $this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __construct(ValidatorInterface $validator)
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, callable $next): void
public function handle(Envelope $envelope, StackInterface $stack): void
{
$message = $envelope->getMessage();
$groups = null;
Expand All @@ -45,6 +45,6 @@ public function handle(Envelope $envelope, callable $next): void
throw new ValidationFailedException($message, $violations);
}

$next($envelope);
$stack->next()->handle($envelope, $stack);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Tests\Fixtures\DummyCommand;
use Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler;
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
Expand Down Expand Up @@ -846,8 +847,8 @@ public function dummyMethodForSomeBus()

class UselessMiddleware implements MiddlewareInterface
{
public function handle(Envelope $message, callable $next): void
public function handle(Envelope $message, StackInterface $stack): void
{
$next($message);
$stack->next()->handle($message, $stack);
}
}
16 changes: 8 additions & 8 deletions src/Symfony/Component/Messenger/Tests/MessageBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public function testItCallsMiddleware()
$firstMiddleware->expects($this->once())
->method('handle')
->with($envelope, $this->anything())
->will($this->returnCallback(function ($envelope, $next) {
$next($envelope);
->will($this->returnCallback(function ($envelope, $stack) {
$stack->next()->handle($envelope, $stack);
}));

$secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
Expand All @@ -76,16 +76,16 @@ public function testThatAMiddlewareCanAddSomeStampsToTheEnvelope()
$firstMiddleware->expects($this->once())
->method('handle')
->with($envelope, $this->anything())
->will($this->returnCallback(function ($envelope, $next) {
$next($envelope->with(new AnEnvelopeStamp()));
->will($this->returnCallback(function ($envelope, $stack) {
$stack->next()->handle($envelope->with(new AnEnvelopeStamp()), $stack);
}));

$secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
$secondMiddleware->expects($this->once())
->method('handle')
->with($envelopeWithAnotherStamp, $this->anything())
->will($this->returnCallback(function ($envelope, $next) {
$next($envelope);
->will($this->returnCallback(function ($envelope, $stack) {
$stack->next()->handle($envelope, $stack);
}));

$thirdMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
Expand Down Expand Up @@ -115,8 +115,8 @@ public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeSta
$firstMiddleware->expects($this->once())
->method('handle')
->with($envelope, $this->anything())
->will($this->returnCallback(function ($message, $next) use ($expectedEnvelope) {
$next($expectedEnvelope);
->will($this->returnCallback(function ($envelope, $stack) use ($expectedEnvelope) {
$stack->next()->handle($expectedEnvelope, $stack);
}));

$secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
Expand Down
Loading
0