Skip to content

Commit 61c2159

Browse files
[Messenger] Add StackInterface, allowing to unstack the call stack
1 parent 556b138 commit 61c2159

20 files changed

+228
-153
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CHANGELOG
77
* The component is not experimental anymore
88
* All the changes below are BC BREAKS
99
* `MessageBusInterface::dispatch()` and `MiddlewareInterface::handle()` now return `void`
10-
* `MiddlewareInterface::handle()` now require an `Envelope` as first argument
10+
* `MiddlewareInterface::handle()` now require an `Envelope` as first argument and a `StackInterface` as second
1111
* `EnvelopeAwareInterface` has been removed
1212
* The signature of `Amqp*` classes changed to take a `Connection` as a first argument and an optional
1313
`Serializer` as a second argument.
@@ -16,7 +16,6 @@ CHANGELOG
1616
Instead, it accepts the sender instance itself instead of its identifier in the container.
1717
* `MessageSubscriberInterface::getHandledMessages()` return value has changed. The value of an array item
1818
needs to be an associative array or the method name.
19-
* `ValidationMiddleware::handle()` and `SendMessageMiddleware::handle()` now require an `Envelope` object
2019
* `StampInterface` replaces `EnvelopeItemInterface` and doesn't extend `Serializable` anymore
2120
* The `ConsumeMessagesCommand` class now takes an instance of `Psr\Container\ContainerInterface`
2221
as first constructor argument

MessageBus.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Messenger;
1313

1414
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
15+
use Symfony\Component\Messenger\Middleware\StackMiddleware;
1516

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

70-
if ($middlewareIterator->valid()) {
71-
$middlewareIterator->current()->handle($envelope, $next);
72-
}
73-
};
74-
75-
$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $next);
70+
$middlewareIterator->current()->handle($message instanceof Envelope ? $message : new Envelope($message), $stack);
7671
}
7772
}

Middleware/ActivationMiddleware.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ public function __construct(MiddlewareInterface $inner, $activated)
3535
/**
3636
* {@inheritdoc}
3737
*/
38-
public function handle(Envelope $envelope, callable $next): void
38+
public function handle(Envelope $envelope, StackInterface $stack): void
3939
{
4040
if (\is_callable($this->activated) ? ($this->activated)($envelope) : $this->activated) {
41-
$this->inner->handle($envelope, $next);
41+
$this->inner->handle($envelope, $stack);
4242
} else {
43-
$next($envelope);
43+
$stack->next()->handle($envelope, $stack);
4444
}
4545
}
4646
}

Middleware/HandleMessageMiddleware.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public function __construct(HandlerLocatorInterface $messageHandlerLocator, bool
3434
*
3535
* @throws NoHandlerForMessageException When no handler is found and $allowNoHandlers is false
3636
*/
37-
public function handle(Envelope $envelope, callable $next): void
37+
public function handle(Envelope $envelope, StackInterface $stack): void
3838
{
3939
if (null !== $handler = $this->messageHandlerLocator->getHandler($envelope)) {
4040
$handler($envelope->getMessage());
41-
$next($envelope);
41+
$stack->next()->handle($envelope, $stack);
4242
} elseif (!$this->allowNoHandlers) {
4343
throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', \get_class($envelope->getMessage())));
4444
}

Middleware/LoggingMiddleware.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function __construct(LoggerInterface $logger)
2929
/**
3030
* {@inheritdoc}
3131
*/
32-
public function handle(Envelope $envelope, callable $next): void
32+
public function handle(Envelope $envelope, StackInterface $stack): void
3333
{
3434
$message = $envelope->getMessage();
3535
$context = array(
@@ -39,7 +39,7 @@ public function handle(Envelope $envelope, callable $next): void
3939
$this->logger->debug('Starting handling message {name}', $context);
4040

4141
try {
42-
$next($envelope);
42+
$stack->next()->handle($envelope, $stack);
4343
} catch (\Throwable $e) {
4444
$context['exception'] = $e;
4545
$this->logger->warning('An exception occurred while handling message {name}', $context);

Middleware/MiddlewareInterface.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,5 @@
1818
*/
1919
interface MiddlewareInterface
2020
{
21-
/**
22-
* @param callable|NextInterface $next
23-
*/
24-
public function handle(Envelope $envelope, callable $next): void;
25-
}
26-
27-
/**
28-
* @internal
29-
*/
30-
interface NextInterface
31-
{
32-
public function __invoke(Envelope $envelope): void;
21+
public function handle(Envelope $envelope, StackInterface $stack): void;
3322
}

Middleware/SendMessageMiddleware.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public function __construct(SenderLocatorInterface $senderLocator, array $messag
3434
/**
3535
* {@inheritdoc}
3636
*/
37-
public function handle(Envelope $envelope, callable $next): void
37+
public function handle(Envelope $envelope, StackInterface $stack): void
3838
{
3939
if ($envelope->get(ReceivedStamp::class)) {
4040
// It's a received message. Do not send it back:
41-
$next($envelope);
41+
$stack->next()->handle($envelope, $stack);
4242

4343
return;
4444
}
@@ -54,6 +54,6 @@ public function handle(Envelope $envelope, callable $next): void
5454
}
5555
}
5656

57-
$next($envelope);
57+
$stack->next()->handle($envelope, $stack);
5858
}
5959
}

Middleware/StackInterface.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Middleware;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
interface StackInterface
18+
{
19+
/**
20+
* Returns the next middleware to process a message.
21+
*/
22+
public function next(): MiddlewareInterface;
23+
}

Middleware/StackMiddleware.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Middleware;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
16+
/**
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class StackMiddleware implements MiddlewareInterface, StackInterface
20+
{
21+
private $middlewareIterator;
22+
23+
public function __construct(\Iterator $middlewareIterator = null)
24+
{
25+
$this->middlewareIterator = $middlewareIterator;
26+
}
27+
28+
public function next(): MiddlewareInterface
29+
{
30+
if (null === $iterator = $this->middlewareIterator) {
31+
return $this;
32+
}
33+
$iterator->next();
34+
35+
if (!$iterator->valid()) {
36+
$this->middlewareIterator = null;
37+
38+
return $this;
39+
}
40+
41+
return $iterator->current();
42+
}
43+
44+
public function handle(Envelope $envelope, StackInterface $stack): void
45+
{
46+
// no-op: this is the last null middleware
47+
}
48+
}

Middleware/TraceableMiddleware.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function __construct(MiddlewareInterface $inner, Stopwatch $stopwatch, st
3737
/**
3838
* {@inheritdoc}
3939
*/
40-
public function handle(Envelope $envelope, callable $next): void
40+
public function handle(Envelope $envelope, StackInterface $stack): void
4141
{
4242
$class = \get_class($this->inner);
4343
$eventName = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
@@ -49,15 +49,52 @@ public function handle(Envelope $envelope, callable $next): void
4949
$this->stopwatch->start($eventName, $this->eventCategory);
5050

5151
try {
52-
$this->inner->handle($envelope, function (Envelope $envelope) use ($next, $eventName) {
53-
$this->stopwatch->stop($eventName);
54-
$next($envelope);
55-
$this->stopwatch->start($eventName, $this->eventCategory);
56-
});
52+
$this->inner->handle($envelope, new TraceableInnerMiddleware($stack, $this->stopwatch, $eventName, $this->eventCategory));
5753
} finally {
5854
if ($this->stopwatch->isStarted($eventName)) {
5955
$this->stopwatch->stop($eventName);
6056
}
6157
}
6258
}
6359
}
60+
61+
/**
62+
* @internal
63+
*/
64+
class TraceableInnerMiddleware implements MiddlewareInterface, StackInterface
65+
{
66+
private $stack;
67+
private $stopwatch;
68+
private $eventName;
69+
private $eventCategory;
70+
71+
public function __construct(StackInterface $stack, Stopwatch $stopwatch, string $eventName, string $eventCategory)
72+
{
73+
$this->stack = $stack;
74+
$this->stopwatch = $stopwatch;
75+
$this->eventName = $eventName;
76+
$this->eventCategory = $eventCategory;
77+
}
78+
79+
/**
80+
* {@inheritdoc}
81+
*/
82+
public function handle(Envelope $envelope, StackInterface $stack): void
83+
{
84+
$this->stopwatch->stop($this->eventName);
85+
if ($this === $stack) {
86+
$this->stack->next()->handle($envelope, $this->stack);
87+
} else {
88+
$stack->next()->handle($envelope, $stack);
89+
}
90+
$this->stopwatch->start($this->eventName, $this->eventCategory);
91+
}
92+
93+
/**
94+
* {@inheritdoc}
95+
*/
96+
public function next(): MiddlewareInterface
97+
{
98+
return $this;
99+
}
100+
}

0 commit comments

Comments
 (0)