feature #28909 [Messenger] made dispatch() and handle() return void (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[Messenger] made dispatch() and handle() return void

| Q             | A
| ------------- | ---
| Branch?       | 4.2
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Middlewares and dispatchers should not return any value. Forwarding back the results from handlers breaks the scope of the component. The canonical example of some bad design this can lead to is `ChainHandler`: how can the caller know that there is one in the chain and that it should expect an array as a return value? More generally, how can a caller know what to expect back from a call to dispatch()? I think we should not allow such broken designs.

Instead, we should favor east-oriented design: if one needs a command bus, one would have to dispatch a proper command object - and if a result is expected back, it should be done via a setter on the dispatched command - or better, a callback set on the command object. This way we play *by the rules* of the type-system, not against.

Commits
-------

f942ffcb1b [Messenger] made dispatch() and handle() return void
This commit is contained in:
Samuel ROZE 2018-10-21 13:42:19 +01:00
commit d901c6d846
28 changed files with 71 additions and 173 deletions

View File

@ -31,7 +31,7 @@ class DoctrineTransactionMiddleware implements MiddlewareInterface
$this->entityManagerName = $entityManagerName; $this->entityManagerName = $entityManagerName;
} }
public function handle($message, callable $next) public function handle($message, callable $next): void
{ {
$entityManager = $this->managerRegistry->getManager($this->entityManagerName); $entityManager = $this->managerRegistry->getManager($this->entityManagerName);
@ -41,7 +41,7 @@ class DoctrineTransactionMiddleware implements MiddlewareInterface
$entityManager->getConnection()->beginTransaction(); $entityManager->getConnection()->beginTransaction();
try { try {
$result = $next($message); $next($message);
$entityManager->flush(); $entityManager->flush();
$entityManager->getConnection()->commit(); $entityManager->getConnection()->commit();
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
@ -49,7 +49,5 @@ class DoctrineTransactionMiddleware implements MiddlewareInterface
throw $exception; throw $exception;
} }
return $result;
} }
} }

View File

@ -168,16 +168,6 @@
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>
<tr>
<td class="text-bold">Result</td>
<td>
{% if dispatchCall.result is defined %}
{{ profiler_dump(dispatchCall.result.seek('value'), maxDepth=2) }}
{% elseif dispatchCall.exception is defined %}
<span class="text-danger">No result as an exception occurred</span>
{% endif %}
</td>
</tr>
{% if dispatchCall.exception is defined %} {% if dispatchCall.exception is defined %}
<tr> <tr>
<td class="text-bold">Exception</td> <td class="text-bold">Exception</td>

View File

@ -38,11 +38,13 @@ class SendMessageMiddleware implements MiddlewareInterface, EnvelopeAwareInterfa
* *
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle($envelope, callable $next) public function handle($envelope, callable $next): void
{ {
if ($envelope->get(ReceivedStamp::class)) { if ($envelope->get(ReceivedStamp::class)) {
// It's a received message. Do not send it back: // It's a received message. Do not send it back:
return $next($envelope); $next($envelope);
return;
} }
$sender = $this->senderLocator->getSenderForMessage($envelope->getMessage()); $sender = $this->senderLocator->getSenderForMessage($envelope->getMessage());
@ -55,7 +57,7 @@ class SendMessageMiddleware implements MiddlewareInterface, EnvelopeAwareInterfa
} }
} }
return $next($envelope); $next($envelope);
} }
private function mustSendAndHandle($message): bool private function mustSendAndHandle($message): bool

View File

@ -6,6 +6,7 @@ CHANGELOG
* The component is not experimental anymore * The component is not experimental anymore
* All the changes below are BC BREAKS * All the changes below are BC BREAKS
* `MessageBusInterface::dispatch()` and `MiddlewareInterface::handle()` now return `void`
* The signature of `Amqp*` classes changed to take a `Connection` as a first argument and an optional * The signature of `Amqp*` classes changed to take a `Connection` as a first argument and an optional
`Serializer` as a second argument. `Serializer` as a second argument.
* `SenderLocator` has been renamed to `ContainerSenderLocator` * `SenderLocator` has been renamed to `ContainerSenderLocator`

View File

@ -98,14 +98,6 @@ class MessengerDataCollector extends DataCollector implements LateDataCollectorI
'caller' => $tracedMessage['caller'], 'caller' => $tracedMessage['caller'],
); );
if (array_key_exists('result', $tracedMessage)) {
$result = $tracedMessage['result'];
$debugRepresentation['result'] = array(
'type' => \is_object($result) ? \get_class($result) : \gettype($result),
'value' => $result,
);
}
if (isset($tracedMessage['exception'])) { if (isset($tracedMessage['exception'])) {
$exception = $tracedMessage['exception']; $exception = $tracedMessage['exception'];

View File

@ -39,12 +39,8 @@ class ChainHandler
public function __invoke($message) public function __invoke($message)
{ {
$results = array();
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
$results[] = $handler($message); $handler($message);
} }
return $results;
} }
} }

View File

@ -38,13 +38,13 @@ class MessageBus implements MessageBusInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function dispatch($message) public function dispatch($message): void
{ {
if (!\is_object($message)) { if (!\is_object($message)) {
throw new InvalidArgumentException(sprintf('Invalid type for message argument. Expected object, but got "%s".', \gettype($message))); throw new InvalidArgumentException(sprintf('Invalid type for message argument. Expected object, but got "%s".', \gettype($message)));
} }
return \call_user_func($this->callableForNextMiddleware(0, Envelope::wrap($message)), $message); \call_user_func($this->callableForNextMiddleware(0, Envelope::wrap($message)), $message);
} }
private function callableForNextMiddleware(int $index, Envelope $currentEnvelope): callable private function callableForNextMiddleware(int $index, Envelope $currentEnvelope): callable
@ -71,7 +71,7 @@ class MessageBus implements MessageBusInterface
$message = $message->getMessage(); $message = $message->getMessage();
} }
return $middleware->handle($message, $this->callableForNextMiddleware($index + 1, $currentEnvelope)); $middleware->handle($message, $this->callableForNextMiddleware($index + 1, $currentEnvelope));
}; };
} }
} }

View File

@ -19,11 +19,7 @@ interface MessageBusInterface
/** /**
* Dispatches the given message. * Dispatches the given message.
* *
* The bus can return a value coming from handlers, but is not required to do so.
*
* @param object|Envelope $message The message or the message pre-wrapped in an envelope * @param object|Envelope $message The message or the message pre-wrapped in an envelope
*
* @return mixed
*/ */
public function dispatch($message); public function dispatch($message): void;
} }

View File

@ -18,10 +18,10 @@ use Symfony\Component\Messenger\Exception\NoHandlerForMessageException;
*/ */
class AllowNoHandlerMiddleware implements MiddlewareInterface class AllowNoHandlerMiddleware implements MiddlewareInterface
{ {
public function handle($message, callable $next) public function handle($message, callable $next): void
{ {
try { try {
return $next($message); $next($message);
} catch (NoHandlerForMessageException $e) { } catch (NoHandlerForMessageException $e) {
// We allow not having a handler for this message. // We allow not having a handler for this message.
} }

View File

@ -37,12 +37,12 @@ class ActivationMiddlewareDecorator implements MiddlewareInterface, EnvelopeAwar
/** /**
* @param Envelope $envelope * @param Envelope $envelope
*/ */
public function handle($envelope, callable $next) public function handle($envelope, callable $next): void
{ {
if (\is_callable($this->activated) ? ($this->activated)($envelope) : $this->activated) { if (\is_callable($this->activated) ? ($this->activated)($envelope) : $this->activated) {
return $this->inner->handle($envelope->getMessageFor($this->inner), $next); $this->inner->handle($envelope->getMessageFor($this->inner), $next);
} else {
$next($envelope);
} }
return $next($envelope);
} }
} }

View File

@ -39,7 +39,7 @@ class TraceableMiddleware implements MiddlewareInterface, EnvelopeAwareInterface
/** /**
* @param Envelope $envelope * @param Envelope $envelope
*/ */
public function handle($envelope, callable $next) public function handle($envelope, callable $next): void
{ {
$class = \get_class($this->inner); $class = \get_class($this->inner);
$eventName = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; $eventName = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
@ -51,19 +51,15 @@ class TraceableMiddleware implements MiddlewareInterface, EnvelopeAwareInterface
$this->stopwatch->start($eventName, $this->eventCategory); $this->stopwatch->start($eventName, $this->eventCategory);
try { try {
$result = $this->inner->handle($envelope->getMessageFor($this->inner), function ($message) use ($next, $eventName) { $this->inner->handle($envelope->getMessageFor($this->inner), function ($message) use ($next, $eventName) {
$this->stopwatch->stop($eventName); $this->stopwatch->stop($eventName);
$result = $next($message); $next($message);
$this->stopwatch->start($eventName, $this->eventCategory); $this->stopwatch->start($eventName, $this->eventCategory);
return $result;
}); });
} finally { } finally {
if ($this->stopwatch->isStarted($eventName)) { if ($this->stopwatch->isStarted($eventName)) {
$this->stopwatch->stop($eventName); $this->stopwatch->stop($eventName);
} }
} }
return $result;
} }
} }

View File

@ -28,13 +28,11 @@ class HandleMessageMiddleware implements MiddlewareInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle($message, callable $next) public function handle($message, callable $next): void
{ {
$handler = $this->messageHandlerResolver->resolve($message); $handler = $this->messageHandlerResolver->resolve($message);
$result = $handler($message); $handler($message);
$next($message); $next($message);
return $result;
} }
} }

View File

@ -28,12 +28,12 @@ class LoggingMiddleware implements MiddlewareInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle($message, callable $next) public function handle($message, callable $next): void
{ {
$this->logger->debug('Starting handling message {class}', $this->createContext($message)); $this->logger->debug('Starting handling message {class}', $this->createContext($message));
try { try {
$result = $next($message); $next($message);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('An exception occurred while handling message {class}', array_merge( $this->logger->warning('An exception occurred while handling message {class}', array_merge(
$this->createContext($message), $this->createContext($message),
@ -44,8 +44,6 @@ class LoggingMiddleware implements MiddlewareInterface
} }
$this->logger->debug('Finished handling message {class}', $this->createContext($message)); $this->logger->debug('Finished handling message {class}', $this->createContext($message));
return $result;
} }
private function createContext($message): array private function createContext($message): array

View File

@ -18,8 +18,6 @@ interface MiddlewareInterface
{ {
/** /**
* @param object $message * @param object $message
*
* @return mixed
*/ */
public function handle($message, callable $next); public function handle($message, callable $next): void;
} }

View File

@ -32,7 +32,7 @@ class ValidationMiddleware implements MiddlewareInterface, EnvelopeAwareInterfac
/** /**
* @param Envelope $envelope * @param Envelope $envelope
*/ */
public function handle($envelope, callable $next) public function handle($envelope, callable $next): void
{ {
$message = $envelope->getMessage(); $message = $envelope->getMessage();
$groups = null; $groups = null;
@ -46,6 +46,6 @@ class ValidationMiddleware implements MiddlewareInterface, EnvelopeAwareInterfac
throw new ValidationFailedException($message, $violations); throw new ValidationFailedException($message, $violations);
} }
return $next($envelope); $next($envelope);
} }
} }

View File

@ -33,15 +33,12 @@ class MessengerDataCollectorTest extends TestCase
$this->dumper->setColors(false); $this->dumper->setColors(false);
} }
/** public function testHandle()
* @dataProvider getHandleTestData
*/
public function testHandle($returnedValue, $expected)
{ {
$message = new DummyMessage('dummy message'); $message = new DummyMessage('dummy message');
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
$bus->method('dispatch')->with($message)->willReturn($returnedValue); $bus->method('dispatch')->with($message);
$bus = new TraceableMessageBus($bus); $bus = new TraceableMessageBus($bus);
$collector = new MessengerDataCollector(); $collector = new MessengerDataCollector();
@ -54,13 +51,9 @@ class MessengerDataCollectorTest extends TestCase
$messages = $collector->getMessages(); $messages = $collector->getMessages();
$this->assertCount(1, $messages); $this->assertCount(1, $messages);
$this->assertStringMatchesFormat($expected, $this->getDataAsString($messages[0]));
}
public function getHandleTestData()
{
$file = __FILE__; $file = __FILE__;
$messageDump = <<<DUMP $expected = <<<DUMP
array:4 [
"bus" => "default" "bus" => "default"
"stamps" => null "stamps" => null
"message" => array:2 [ "message" => array:2 [
@ -74,48 +67,10 @@ class MessengerDataCollectorTest extends TestCase
"file" => "$file" "file" => "$file"
"line" => %d "line" => %d
] ]
]
DUMP; DUMP;
yield 'no returned value' => array( $this->assertStringMatchesFormat($expected, $this->getDataAsString($messages[0]));
null,
<<<DUMP
array:5 [
$messageDump
"result" => array:2 [
"type" => "NULL"
"value" => null
]
]
DUMP
);
yield 'scalar returned value' => array(
'returned value',
<<<DUMP
array:5 [
$messageDump
"result" => array:2 [
"type" => "string"
"value" => "returned value"
]
]
DUMP
);
yield 'array returned value' => array(
array('returned value'),
<<<DUMP
array:5 [
$messageDump
"result" => array:2 [
"type" => "array"
"value" => array:1 [
0 => "returned value"
]
]
]
DUMP
);
} }
public function testHandleWithException() public function testHandleWithException()

View File

@ -852,8 +852,8 @@ class HandlerOnUndefinedBus implements MessageSubscriberInterface
class UselessMiddleware implements MiddlewareInterface class UselessMiddleware implements MiddlewareInterface
{ {
public function handle($message, callable $next) public function handle($message, callable $next): void
{ {
return $next($message); $next($message);
} }
} }

View File

@ -17,7 +17,7 @@ use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
class ChainHandlerTest extends TestCase class ChainHandlerTest extends TestCase
{ {
public function testItCallsTheHandlersAndReturnsAllResults() public function testItCallsTheHandlers()
{ {
$message = new DummyMessage('Hey'); $message = new DummyMessage('Hey');
@ -26,19 +26,15 @@ class ChainHandlerTest extends TestCase
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($message) ->with($message)
->willReturn('Hello')
; ;
$handler2 = $this->createPartialMock(\stdClass::class, array('__invoke')); $handler2 = $this->createPartialMock(\stdClass::class, array('__invoke'));
$handler2 $handler2
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($message) ->with($message)
->willReturn('World')
; ;
$results = (new ChainHandler(array($handler1, $handler2)))($message); (new ChainHandler(array($handler1, $handler2)))($message);
$this->assertSame(array('Hello', 'World'), $results);
} }
/** /**

View File

@ -39,7 +39,7 @@ class MessageBusTest extends TestCase
(new MessageBus())->dispatch('wrong'); (new MessageBus())->dispatch('wrong');
} }
public function testItCallsMiddlewareAndChainTheReturnValue() public function testItCallsMiddleware()
{ {
$message = new DummyMessage('Hello'); $message = new DummyMessage('Hello');
$responseFromDepthMiddleware = 1234; $responseFromDepthMiddleware = 1234;
@ -49,21 +49,21 @@ class MessageBusTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, $next) { ->will($this->returnCallback(function ($message, $next) {
return $next($message); $next($message);
})); }));
$secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); $secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
$secondMiddleware->expects($this->once()) $secondMiddleware->expects($this->once())
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->willReturn($responseFromDepthMiddleware); ;
$bus = new MessageBus(array( $bus = new MessageBus(array(
$firstMiddleware, $firstMiddleware,
$secondMiddleware, $secondMiddleware,
)); ));
$this->assertEquals($responseFromDepthMiddleware, $bus->dispatch($message)); $bus->dispatch($message);
} }
public function testItKeepsTheEnvelopeEvenThroughAMiddlewareThatIsNotEnvelopeAware() public function testItKeepsTheEnvelopeEvenThroughAMiddlewareThatIsNotEnvelopeAware()
@ -76,7 +76,7 @@ class MessageBusTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, $next) { ->will($this->returnCallback(function ($message, $next) {
return $next($message); $next($message);
})); }));
$secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); $secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock();
@ -104,7 +104,7 @@ class MessageBusTest extends TestCase
->method('handle') ->method('handle')
->with($envelope, $this->anything()) ->with($envelope, $this->anything())
->will($this->returnCallback(function ($message, $next) { ->will($this->returnCallback(function ($message, $next) {
return $next($message->with(new AnEnvelopeStamp())); $next($message->with(new AnEnvelopeStamp()));
})); }));
$secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); $secondMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
@ -112,7 +112,7 @@ class MessageBusTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, $next) { ->will($this->returnCallback(function ($message, $next) {
return $next($message); $next($message);
})); }));
$thirdMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); $thirdMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock();
@ -143,7 +143,7 @@ class MessageBusTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, $next) use ($changedMessage) { ->will($this->returnCallback(function ($message, $next) use ($changedMessage) {
return $next($changedMessage); $next($changedMessage);
})); }));
$secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock(); $secondMiddleware = $this->getMockBuilder(array(MiddlewareInterface::class, EnvelopeAwareInterface::class))->getMock();

View File

@ -18,15 +18,15 @@ use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
class AllowNoHandlerMiddlewareTest extends TestCase class AllowNoHandlerMiddlewareTest extends TestCase
{ {
public function testItCallsNextMiddlewareAndReturnsItsResult() public function testItCallsNextMiddleware()
{ {
$message = new DummyMessage('Hey'); $message = new DummyMessage('Hey');
$next = $this->createPartialMock(\stdClass::class, array('__invoke')); $next = $this->createPartialMock(\stdClass::class, array('__invoke'));
$next->expects($this->once())->method('__invoke')->with($message)->willReturn('Foo'); $next->expects($this->once())->method('__invoke')->with($message);
$middleware = new AllowNoHandlerMiddleware(); $middleware = new AllowNoHandlerMiddleware();
$this->assertSame('Foo', $middleware->handle($message, $next)); $middleware->handle($message, $next);
} }
public function testItCatchesTheNoHandlerException() public function testItCatchesTheNoHandlerException()

View File

@ -32,11 +32,11 @@ class ActivationMiddlewareDecoratorTest extends TestCase
$next->expects($this->never())->method('__invoke'); $next->expects($this->never())->method('__invoke');
$middleware = $this->createMock(MiddlewareInterface::class); $middleware = $this->createMock(MiddlewareInterface::class);
$middleware->expects($this->once())->method('handle')->with($message, $next)->willReturn('Hello from middleware'); $middleware->expects($this->once())->method('handle')->with($message, $next);
$decorator = new ActivationMiddlewareDecorator($middleware, true); $decorator = new ActivationMiddlewareDecorator($middleware, true);
$this->assertSame('Hello from middleware', $decorator->handle($envelope, $next)); $decorator->handle($envelope, $next);
} }
public function testExecuteMiddlewareOnActivatedWithCallable() public function testExecuteMiddlewareOnActivatedWithCallable()
@ -51,11 +51,11 @@ class ActivationMiddlewareDecoratorTest extends TestCase
$next->expects($this->never())->method('__invoke'); $next->expects($this->never())->method('__invoke');
$middleware = $this->createMock(MiddlewareInterface::class); $middleware = $this->createMock(MiddlewareInterface::class);
$middleware->expects($this->once())->method('handle')->with($message, $next)->willReturn('Hello from middleware'); $middleware->expects($this->once())->method('handle')->with($message, $next);
$decorator = new ActivationMiddlewareDecorator($middleware, $activated); $decorator = new ActivationMiddlewareDecorator($middleware, $activated);
$this->assertSame('Hello from middleware', $decorator->handle($envelope, $next)); $decorator->handle($envelope, $next);
} }
public function testExecuteEnvelopeAwareMiddlewareWithEnvelope() public function testExecuteEnvelopeAwareMiddlewareWithEnvelope()
@ -67,11 +67,11 @@ class ActivationMiddlewareDecoratorTest extends TestCase
$next->expects($this->never())->method('__invoke'); $next->expects($this->never())->method('__invoke');
$middleware = $this->createMock(array(MiddlewareInterface::class, EnvelopeAwareInterface::class)); $middleware = $this->createMock(array(MiddlewareInterface::class, EnvelopeAwareInterface::class));
$middleware->expects($this->once())->method('handle')->with($envelope, $next)->willReturn('Hello from middleware'); $middleware->expects($this->once())->method('handle')->with($envelope, $next);
$decorator = new ActivationMiddlewareDecorator($middleware, true); $decorator = new ActivationMiddlewareDecorator($middleware, true);
$this->assertSame('Hello from middleware', $decorator->handle($envelope, $next)); $decorator->handle($envelope, $next);
} }
public function testExecuteMiddlewareOnDeactivated() public function testExecuteMiddlewareOnDeactivated()
@ -80,13 +80,13 @@ class ActivationMiddlewareDecoratorTest extends TestCase
$envelope = Envelope::wrap($message); $envelope = Envelope::wrap($message);
$next = $this->createPartialMock(\stdClass::class, array('__invoke')); $next = $this->createPartialMock(\stdClass::class, array('__invoke'));
$next->expects($this->once())->method('__invoke')->with($envelope)->willReturn('Hello from $next'); $next->expects($this->once())->method('__invoke')->with($envelope);
$middleware = $this->createMock(MiddlewareInterface::class); $middleware = $this->createMock(MiddlewareInterface::class);
$middleware->expects($this->never())->method('handle'); $middleware->expects($this->never())->method('handle');
$decorator = new ActivationMiddlewareDecorator($middleware, false); $decorator = new ActivationMiddlewareDecorator($middleware, false);
$this->assertSame('Hello from $next', $decorator->handle($envelope, $next)); $decorator->handle($envelope, $next);
} }
} }

View File

@ -33,7 +33,7 @@ class TraceableMiddlewareTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, callable $next) { ->will($this->returnCallback(function ($message, callable $next) {
return $next($message); $next($message);
})) }))
; ;
@ -42,7 +42,6 @@ class TraceableMiddlewareTest extends TestCase
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($message) ->with($message)
->willReturn($expectedReturnedValue = 'Hello')
; ;
$stopwatch = $this->createMock(Stopwatch::class); $stopwatch = $this->createMock(Stopwatch::class);
@ -58,7 +57,7 @@ class TraceableMiddlewareTest extends TestCase
$traced = new TraceableMiddleware($middleware, $stopwatch, $busId); $traced = new TraceableMiddleware($middleware, $stopwatch, $busId);
$this->assertSame($expectedReturnedValue, $traced->handle($envelope, $next)); $traced->handle($envelope, $next);
} }
/** /**
@ -75,7 +74,7 @@ class TraceableMiddlewareTest extends TestCase
->method('handle') ->method('handle')
->with($message, $this->anything()) ->with($message, $this->anything())
->will($this->returnCallback(function ($message, callable $next) { ->will($this->returnCallback(function ($message, callable $next) {
return $next($message); $next($message);
})) }))
; ;

View File

@ -23,7 +23,6 @@ class HandleMessageMiddlewareTest extends TestCase
$message = new DummyMessage('Hey'); $message = new DummyMessage('Hey');
$handler = $this->createPartialMock(\stdClass::class, array('__invoke')); $handler = $this->createPartialMock(\stdClass::class, array('__invoke'));
$handler->method('__invoke')->willReturn('Hello');
$next = $this->createPartialMock(\stdClass::class, array('__invoke')); $next = $this->createPartialMock(\stdClass::class, array('__invoke'));

View File

@ -32,12 +32,9 @@ class LoggingMiddlewareTest extends TestCase
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($message) ->with($message)
->willReturn('Hello')
; ;
$result = (new LoggingMiddleware($logger))->handle($message, $next); (new LoggingMiddleware($logger))->handle($message, $next);
$this->assertSame('Hello', $result);
} }
/** /**

View File

@ -38,12 +38,9 @@ class ValidationMiddlewareTest extends TestCase
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($envelope) ->with($envelope)
->willReturn('Hello')
; ;
$result = (new ValidationMiddleware($validator))->handle($envelope, $next); (new ValidationMiddleware($validator))->handle($envelope, $next);
$this->assertSame('Hello', $result);
} }
public function testValidateWithStampAndNextMiddleware() public function testValidateWithStampAndNextMiddleware()
@ -61,12 +58,9 @@ class ValidationMiddlewareTest extends TestCase
->expects($this->once()) ->expects($this->once())
->method('__invoke') ->method('__invoke')
->with($envelope) ->with($envelope)
->willReturn('Hello')
; ;
$result = (new ValidationMiddleware($validator))->handle($envelope, $next); (new ValidationMiddleware($validator))->handle($envelope, $next);
$this->assertSame('Hello', $result);
} }
/** /**

View File

@ -20,20 +20,18 @@ use Symfony\Component\Messenger\TraceableMessageBus;
class TraceableMessageBusTest extends TestCase class TraceableMessageBusTest extends TestCase
{ {
public function testItTracesResult() public function testItTracesDispatch()
{ {
$message = new DummyMessage('Hello'); $message = new DummyMessage('Hello');
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
$bus->expects($this->once())->method('dispatch')->with($message)->willReturn($result = array('foo' => 'bar'));
$traceableBus = new TraceableMessageBus($bus); $traceableBus = new TraceableMessageBus($bus);
$line = __LINE__ + 1; $line = __LINE__ + 1;
$this->assertSame($result, $traceableBus->dispatch($message)); $this->assertNull($traceableBus->dispatch($message));
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
$this->assertArraySubset(array( $this->assertArraySubset(array(
'message' => $message, 'message' => $message,
'result' => $result,
'stamps' => null, 'stamps' => null,
'caller' => array( 'caller' => array(
'name' => 'TraceableMessageBusTest.php', 'name' => 'TraceableMessageBusTest.php',
@ -43,20 +41,18 @@ class TraceableMessageBusTest extends TestCase
), $tracedMessages[0], true); ), $tracedMessages[0], true);
} }
public function testItTracesResultWithEnvelope() public function testItTracesDispatchWithEnvelope()
{ {
$envelope = Envelope::wrap($message = new DummyMessage('Hello'))->with($stamp = new AnEnvelopeStamp()); $envelope = Envelope::wrap($message = new DummyMessage('Hello'))->with($stamp = new AnEnvelopeStamp());
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
$bus->expects($this->once())->method('dispatch')->with($envelope)->willReturn($result = array('foo' => 'bar'));
$traceableBus = new TraceableMessageBus($bus); $traceableBus = new TraceableMessageBus($bus);
$line = __LINE__ + 1; $line = __LINE__ + 1;
$this->assertSame($result, $traceableBus->dispatch($envelope)); $this->assertNull($traceableBus->dispatch($envelope));
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages()); $this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
$this->assertArraySubset(array( $this->assertArraySubset(array(
'message' => $message, 'message' => $message,
'result' => $result,
'stamps' => array($stamp), 'stamps' => array($stamp),
'caller' => array( 'caller' => array(
'name' => 'TraceableMessageBusTest.php', 'name' => 'TraceableMessageBusTest.php',

View File

@ -29,7 +29,7 @@ $connection = Connection::fromDsn(getenv('DSN'));
$receiver = new AmqpReceiver($connection, $serializer); $receiver = new AmqpReceiver($connection, $serializer);
$worker = new Worker($receiver, new class() implements MessageBusInterface { $worker = new Worker($receiver, new class() implements MessageBusInterface {
public function dispatch($envelope) public function dispatch($envelope): void
{ {
echo 'Get envelope with message: '.get_class($envelope->getMessage())."\n"; echo 'Get envelope with message: '.get_class($envelope->getMessage())."\n";
echo sprintf("with stamps: %s\n", json_encode(array_keys($envelope->all()), JSON_PRETTY_PRINT)); echo sprintf("with stamps: %s\n", json_encode(array_keys($envelope->all()), JSON_PRETTY_PRINT));

View File

@ -27,7 +27,7 @@ class TraceableMessageBus implements MessageBusInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function dispatch($message) public function dispatch($message): void
{ {
$caller = $this->getCaller(); $caller = $this->getCaller();
$callTime = microtime(true); $callTime = microtime(true);
@ -35,17 +35,14 @@ class TraceableMessageBus implements MessageBusInterface
$stamps = $message instanceof Envelope ? array_values($message->all()) : null; $stamps = $message instanceof Envelope ? array_values($message->all()) : null;
try { try {
$result = $this->decoratedBus->dispatch($message); $this->decoratedBus->dispatch($message);
$this->dispatchedMessages[] = array( $this->dispatchedMessages[] = array(
'stamps' => $stamps, 'stamps' => $stamps,
'message' => $messageToTrace, 'message' => $messageToTrace,
'result' => $result,
'callTime' => $callTime, 'callTime' => $callTime,
'caller' => $caller, 'caller' => $caller,
); );
return $result;
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->dispatchedMessages[] = array( $this->dispatchedMessages[] = array(
'stamps' => $stamps, 'stamps' => $stamps,