Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ab3d019

Browse files
committed
Fixing a bug where a transport could receive a message and dispatch it to a different bus
1 parent 522594a commit ab3d019

File tree

10 files changed

+238
-31
lines changed

10 files changed

+238
-31
lines changed

UPGRADE-4.3.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ HttpKernel
7272
Messenger
7373
---------
7474

75+
* Deprecated the `--bus` option of `ConsumeMessagesCommand`: the
76+
bus is now automatically determined.
7577
* `Amqp` transport does not throw `\AMQPException` anymore, catch `TransportException` instead.
7678
* Deprecated the `LoggingMiddleware` class, pass a logger to `SendMessageMiddleware` instead.
7779

UPGRADE-5.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ HttpKernel
218218
Messenger
219219
---------
220220

221+
* Removed the `--bus` option of `ConsumeMessagesCommand`. Stop
222+
passing the option: the bus is now automatically determined.
221223
* The `LoggingMiddleware` class has been removed, pass a logger to `SendMessageMiddleware` instead.
222224

223225
Monolog

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,8 +1616,14 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
16161616
}
16171617

16181618
$defaultMiddleware = [
1619-
'before' => [['id' => 'dispatch_after_current_bus']],
1620-
'after' => [['id' => 'send_message'], ['id' => 'handle_message']],
1619+
'before' => [
1620+
['id' => 'add_bus_name_stamp_middleware'],
1621+
['id' => 'dispatch_after_current_bus'],
1622+
],
1623+
'after' => [
1624+
['id' => 'send_message'],
1625+
['id' => 'handle_message'],
1626+
],
16211627
];
16221628
foreach ($config['buses'] as $busId => $bus) {
16231629
$middleware = $bus['middleware'];
@@ -1628,6 +1634,10 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
16281634
} else {
16291635
unset($defaultMiddleware['after'][1]['arguments']);
16301636
}
1637+
1638+
// argument to add_bus_name_stamp_middleware
1639+
$defaultMiddleware['before'][0]['arguments'] = [$busId];
1640+
16311641
$middleware = array_merge($defaultMiddleware['before'], $middleware, $defaultMiddleware['after']);
16321642
}
16331643

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
</call>
4040
</service>
4141

42+
<service id="messenger.middleware.add_bus_name_stamp_middleware" class="Symfony\Component\Messenger\Middleware\AddBusNameStampMiddleware" abstract="true" />
43+
4244
<service id="messenger.middleware.dispatch_after_current_bus" class="Symfony\Component\Messenger\Middleware\DispatchAfterCurrentBusMiddleware" />
4345

4446
<service id="messenger.middleware.validation" class="Symfony\Component\Messenger\Middleware\ValidationMiddleware">

src/Symfony/Component/Messenger/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ CHANGELOG
44
4.3.0
55
-----
66

7+
* New classes: `RoutableMessageBus`, `AddBusNameStampMiddleware`
8+
and `BusNameStamp` were added, which allow you to add a bus identifier
9+
to the `Envelope` then find the correct bus when receiving from
10+
the transport. See `ConsumeMessagesCommand`.
11+
* The `ConsumeMessagesCommand` no longer accepts a bus argument
12+
and an optional constructor argument was removed.
713
* Added `PhpSerializer` which uses PHP's native `serialize()` and
814
`unserialize()` to serialize messages to a transport
915
* [BC BREAK] If no serializer were passed, the default serializer

src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Console\Input\InputOption;
2121
use Symfony\Component\Console\Output\OutputInterface;
2222
use Symfony\Component\Console\Style\SymfonyStyle;
23+
use Symfony\Component\Messenger\RoutableMessageBus;
2324
use Symfony\Component\Messenger\Transport\Receiver\StopWhenMemoryUsageIsExceededReceiver;
2425
use Symfony\Component\Messenger\Transport\Receiver\StopWhenMessageCountIsExceededReceiver;
2526
use Symfony\Component\Messenger\Transport\Receiver\StopWhenTimeLimitIsReachedReceiver;
@@ -38,15 +39,13 @@ class ConsumeMessagesCommand extends Command
3839
private $receiverLocator;
3940
private $logger;
4041
private $receiverNames;
41-
private $busNames;
4242

43-
public function __construct(ContainerInterface $busLocator, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = [], array $busNames = [])
43+
public function __construct(ContainerInterface $busLocator, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = [])
4444
{
4545
$this->busLocator = $busLocator;
4646
$this->receiverLocator = $receiverLocator;
4747
$this->logger = $logger;
4848
$this->receiverNames = $receiverNames;
49-
$this->busNames = $busNames;
5049

5150
parent::__construct();
5251
}
@@ -57,15 +56,14 @@ public function __construct(ContainerInterface $busLocator, ContainerInterface $
5756
protected function configure(): void
5857
{
5958
$defaultReceiverName = 1 === \count($this->receiverNames) ? current($this->receiverNames) : null;
60-
$defaultBusName = 1 === \count($this->busNames) ? current($this->busNames) : null;
6159

6260
$this
6361
->setDefinition([
6462
new InputArgument('receiver', $defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $defaultReceiverName),
6563
new InputOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit the number of received messages'),
6664
new InputOption('memory-limit', 'm', InputOption::VALUE_REQUIRED, 'The memory limit the worker can consume'),
6765
new InputOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'The time limit in seconds the worker can run'),
68-
new InputOption('bus', 'b', InputOption::VALUE_REQUIRED, 'Name of the bus to which received messages should be dispatched', $defaultBusName),
66+
new InputOption('bus', 'b', InputOption::VALUE_REQUIRED, '(deprecated) Name of the bus to which received messages should be dispatched'),
6967
])
7068
->setDescription('Consumes messages')
7169
->setHelp(<<<'EOF'
@@ -107,41 +105,28 @@ protected function interact(InputInterface $input, OutputInterface $output)
107105
}
108106
}
109107
}
110-
111-
$busName = $input->getOption('bus');
112-
if ($this->busNames && !$this->busLocator->has($busName)) {
113-
if (null === $busName) {
114-
$io->block('Missing bus argument.', null, 'error', ' ', true);
115-
$input->setOption('bus', $io->choice('Select one of the available buses', $this->busNames));
116-
} elseif ($alternatives = $this->findAlternatives($busName, $this->busNames)) {
117-
$io->block(sprintf('Bus "%s" is not defined.', $busName), null, 'error', ' ', true);
118-
119-
if (1 === \count($alternatives)) {
120-
if ($io->confirm(sprintf('Do you want to dispatch to "%s" instead? ', $alternatives[0]), true)) {
121-
$input->setOption('bus', $alternatives[0]);
122-
}
123-
} else {
124-
$input->setOption('bus', $io->choice('Did you mean one of the following buses instead?', $alternatives, $alternatives[0]));
125-
}
126-
}
127-
}
128108
}
129109

130110
/**
131111
* {@inheritdoc}
132112
*/
133113
protected function execute(InputInterface $input, OutputInterface $output): void
134114
{
135-
if (!$this->receiverLocator->has($receiverName = $input->getArgument('receiver'))) {
136-
throw new RuntimeException(sprintf('Receiver "%s" does not exist.', $receiverName));
115+
if (null !== $input->getOption('bus')) {
116+
$message = sprintf('The "bus" option to the "%s" command was deprecated in Symfony 4.3 and will be removed in 5.0. The bus is now automatically determined.', $this->getName());
117+
@trigger_error($message, E_USER_DEPRECATED);
118+
$output->writeln(sprintf('<comment>%s</comment>', $message));
119+
120+
$bus = $this->busLocator->get($input->getOption('bus'));
121+
} else {
122+
$bus = new RoutableMessageBus($this->busLocator);
137123
}
138124

139-
if (!$this->busLocator->has($busName = $input->getOption('bus'))) {
140-
throw new RuntimeException(sprintf('Bus "%s" does not exist.', $busName));
125+
if (!$this->receiverLocator->has($receiverName = $input->getArgument('receiver'))) {
126+
throw new RuntimeException(sprintf('Receiver "%s" does not exist.', $receiverName));
141127
}
142128

143129
$receiver = $this->receiverLocator->get($receiverName);
144-
$bus = $this->busLocator->get($busName);
145130

146131
$stopsWhen = [];
147132
if ($limit = $input->getOption('limit')) {
@@ -160,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output): void
160145
}
161146

162147
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
163-
$io->success(sprintf('Consuming messages from transport "%s" on bus "%s".', $receiverName, $busName));
148+
$io->success(sprintf('Consuming messages from transport "%s".', $receiverName));
164149

165150
if ($stopsWhen) {
166151
$last = array_pop($stopsWhen);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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+
use Symfony\Component\Messenger\Stamp\BusNameStamp;
16+
17+
/**
18+
* Adds the BusNameStamp to the bus.
19+
*
20+
* @experimental in Symfony 4.2
21+
*
22+
* @author Ryan Weaver <[email protected]>
23+
*/
24+
class AddBusNameStampMiddleware implements MiddlewareInterface
25+
{
26+
private $busName;
27+
28+
public function __construct(string $busName)
29+
{
30+
$this->busName = $busName;
31+
}
32+
33+
public function handle(Envelope $envelope, StackInterface $stack): Envelope
34+
{
35+
$envelope = $envelope->with(new BusNameStamp($this->busName));
36+
37+
return $stack->next()->handle($envelope, $stack);
38+
}
39+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
16+
use Symfony\Component\Messenger\Stamp\BusNameStamp;
17+
18+
/**
19+
* Bus of buses that is routable using a BusNameStamp.
20+
*
21+
* This is useful when passed to Worker: messages received
22+
* from the transport can be sent to the correct bus.
23+
*
24+
* @experimental in Symfony 4.2
25+
*
26+
* @author Ryan Weaver <[email protected]>
27+
*/
28+
class RoutableMessageBus implements MessageBusInterface
29+
{
30+
private $busLocator;
31+
32+
/**
33+
* @param ContainerInterface $busLocator A locator full of MessageBusInterface objects
34+
*/
35+
public function __construct(ContainerInterface $busLocator)
36+
{
37+
$this->busLocator = $busLocator;
38+
}
39+
40+
public function dispatch($envelope): Envelope
41+
{
42+
if (!$envelope instanceof Envelope) {
43+
throw new InvalidArgumentException('Messages passed to RoutableMessageBus::dispatch() must be inside an Envelope');
44+
}
45+
46+
/** @var BusNameStamp $busNameStamp */
47+
$busNameStamp = $envelope->last(BusNameStamp::class);
48+
if (null === $busNameStamp) {
49+
throw new InvalidArgumentException('Envelope does not contain a BusNameStamp.');
50+
}
51+
52+
if (!$this->busLocator->has($busNameStamp->getBusName())) {
53+
throw new InvalidArgumentException(sprintf('Invalid bus name "%s" on BusNameStamp.', $busNameStamp->getBusName()));
54+
}
55+
56+
return $this->busLocator->get($busNameStamp->getBusName())->dispatch($envelope);
57+
}
58+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Stamp;
13+
14+
/**
15+
* Stamp used to identify which bus it was passed to.
16+
*
17+
* @experimental in Symfony 4.2
18+
*
19+
* @author Ryan Weaver <[email protected]>
20+
*/
21+
class BusNameStamp implements StampInterface
22+
{
23+
private $busName;
24+
25+
public function __construct(string $busName)
26+
{
27+
$this->busName = $busName;
28+
}
29+
30+
public function getBusName(): string
31+
{
32+
return $this->busName;
33+
}
34+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Psr\Container\ContainerInterface;
16+
use Symfony\Component\Messenger\Envelope;
17+
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
18+
use Symfony\Component\Messenger\MessageBusInterface;
19+
use Symfony\Component\Messenger\RoutableMessageBus;
20+
use Symfony\Component\Messenger\Stamp\BusNameStamp;
21+
22+
class RoutableMessageBusTest extends TestCase
23+
{
24+
public function testItRoutesToTheCorrectBus()
25+
{
26+
$envelope = new Envelope(new \stdClass(), new BusNameStamp('foo_bus'));
27+
28+
$bus1 = $this->createMock(MessageBusInterface::class);
29+
$bus2 = $this->createMock(MessageBusInterface::class);
30+
31+
$container = $this->createMock(ContainerInterface::class);
32+
$container->expects($this->once())->method('has')->with('foo_bus')->willReturn(true);
33+
$container->expects($this->once())->method('get')->will($this->returnValue($bus2));
34+
35+
$bus1->expects($this->never())->method('dispatch');
36+
$bus2->expects($this->once())->method('dispatch')->with($envelope)->willReturn($envelope);
37+
38+
$routableBus = new RoutableMessageBus($container);
39+
$this->assertSame($envelope, $routableBus->dispatch($envelope));
40+
}
41+
42+
public function testItExceptionOnMissingStamp()
43+
{
44+
$this->expectException(InvalidArgumentException::class);
45+
$this->expectExceptionMessage('does not contain a BusNameStamp');
46+
47+
$envelope = new Envelope(new \stdClass());
48+
49+
$container = $this->createMock(ContainerInterface::class);
50+
$container->expects($this->never())->method('has');
51+
52+
$routableBus = new RoutableMessageBus($container);
53+
$routableBus->dispatch($envelope);
54+
}
55+
56+
public function testItExceptionOnBusNotFound()
57+
{
58+
$this->expectException(InvalidArgumentException::class);
59+
$this->expectExceptionMessage('Invalid bus name');
60+
61+
$envelope = new Envelope(new \stdClass(), new BusNameStamp('foo_bus'));
62+
63+
$container = $this->createMock(ContainerInterface::class);
64+
$container->expects($this->once())->method('has')->willReturn(false);
65+
66+
$routableBus = new RoutableMessageBus($container);
67+
$routableBus->dispatch($envelope);
68+
}
69+
}

0 commit comments

Comments
 (0)