Skip to content

Commit a404917

Browse files
committed
[Messenger] Dispatch events before & after each handler
1 parent b04d7b0 commit a404917

File tree

8 files changed

+174
-0
lines changed

8 files changed

+174
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
->abstract()
8383
->args([
8484
abstract_arg('bus handler resolver'),
85+
false,
86+
service('event_dispatcher'),
8587
])
8688
->tag('monolog.logger', ['channel' => 'messenger'])
8789
->call('setLogger', [service('logger')->ignoreOnInvalid()])

src/Symfony/Component/Messenger/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CHANGELOG
2626
* Add `WrappedExceptionsInterface` interface for exceptions that hold multiple individual exceptions
2727
* Deprecate `HandlerFailedException::getNestedExceptions()`, `HandlerFailedException::getNestedExceptionsOfClass()`
2828
and `DelayedMessageHandlingException::getExceptions()` which are replaced by a new `getWrappedExceptions()` method
29+
* New events: `HandlerStartingEvent`, `HandlerSuccessEvent`, `HandlerFailureEvent`
2930

3031
6.3
3132
---
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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\Event;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
16+
abstract class AbstractHandlerEvent
17+
{
18+
private Envelope $envelope;
19+
private string $handlerName;
20+
21+
public function __construct(Envelope $envelope, string $handlerName)
22+
{
23+
$this->envelope = $envelope;
24+
$this->handlerName = $handlerName;
25+
}
26+
27+
public function getEnvelope(): Envelope
28+
{
29+
return $this->envelope;
30+
}
31+
32+
public function getHandlerName(): string
33+
{
34+
return $this->handlerName;
35+
}
36+
}
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 <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\Event;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
16+
/**
17+
* Event dispatched after a handler fails.
18+
*/
19+
final class HandlerFailureEvent extends AbstractHandlerEvent
20+
{
21+
private \Throwable $exception;
22+
23+
public function __construct(Envelope $envelope, string $handlerName, \Throwable $exception)
24+
{
25+
parent::__construct($envelope, $handlerName);
26+
27+
$this->exception = $exception;
28+
}
29+
30+
public function getException(): \Throwable
31+
{
32+
return $this->exception;
33+
}
34+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\Event;
13+
14+
/**
15+
* Event dispatched before a handler is called.
16+
*/
17+
final class HandlerStartingEvent extends AbstractHandlerEvent
18+
{
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\Event;
13+
14+
/**
15+
* Event dispatched after a handler succeeds.
16+
*/
17+
final class HandlerSuccessEvent extends AbstractHandlerEvent
18+
{
19+
}

src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111

1212
namespace Symfony\Component\Messenger\Middleware;
1313

14+
use Psr\EventDispatcher\EventDispatcherInterface;
1415
use Psr\Log\LoggerAwareTrait;
1516
use Symfony\Component\Messenger\Envelope;
17+
use Symfony\Component\Messenger\Event\HandlerFailureEvent;
18+
use Symfony\Component\Messenger\Event\HandlerStartingEvent;
19+
use Symfony\Component\Messenger\Event\HandlerSuccessEvent;
1620
use Symfony\Component\Messenger\Exception\HandlerFailedException;
1721
use Symfony\Component\Messenger\Exception\LogicException;
1822
use Symfony\Component\Messenger\Exception\NoHandlerForMessageException;
@@ -35,6 +39,7 @@ class HandleMessageMiddleware implements MiddlewareInterface
3539
public function __construct(
3640
private HandlersLocatorInterface $handlersLocator,
3741
private bool $allowNoHandlers = false,
42+
private ?EventDispatcherInterface $eventDispatcher = null,
3843
) {
3944
}
4045

@@ -58,6 +63,10 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
5863
continue;
5964
}
6065

66+
$this->eventDispatcher?->dispatch(new HandlerStartingEvent($envelope, $handlerDescriptor->getName()));
67+
68+
$e = null;
69+
6170
try {
6271
$handler = $handlerDescriptor->getHandler();
6372
$batchHandler = $handlerDescriptor->getBatchHandler();
@@ -97,6 +106,14 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
97106
} catch (\Throwable $e) {
98107
$exceptions[$handlerDescriptor->getName()] = $e;
99108
}
109+
110+
if (null !== $this->eventDispatcher) {
111+
$event = (null !== $e)
112+
? new HandlerFailureEvent($envelope, $handlerDescriptor->getName(), $e)
113+
: new HandlerSuccessEvent($envelope, $handlerDescriptor->getName());
114+
115+
$this->eventDispatcher->dispatch($event);
116+
}
100117
}
101118

102119
/** @var FlushBatchHandlersStamp $flushStamp */

src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111

1212
namespace Symfony\Component\Messenger\Tests\Middleware;
1313

14+
use Psr\EventDispatcher\EventDispatcherInterface;
1415
use Symfony\Component\Messenger\Envelope;
16+
use Symfony\Component\Messenger\Event\HandlerFailureEvent;
17+
use Symfony\Component\Messenger\Event\HandlerStartingEvent;
18+
use Symfony\Component\Messenger\Event\HandlerSuccessEvent;
1519
use Symfony\Component\Messenger\Exception\HandlerFailedException;
1620
use Symfony\Component\Messenger\Exception\LogicException;
1721
use Symfony\Component\Messenger\Exception\NoHandlerForMessageException;
@@ -356,6 +360,48 @@ public function testHandlerArgumentsStampNamedArgument()
356360

357361
$middleware->handle($envelope, $this->getStackMock());
358362
}
363+
364+
public function testDispatchHandlerEvents()
365+
{
366+
$message = new DummyMessage('Hey');
367+
$envelope = new Envelope($message);
368+
369+
$successHandler = $this->createMock(HandleMessageMiddlewareTestCallable::class);
370+
$successHandler->expects($this->once())->method('__invoke');
371+
372+
$failureHandler = $this->createMock(HandleMessageMiddlewareTestCallable::class);
373+
$failureHandler->expects($this->once())->method('__invoke')->willThrowException(
374+
$exception = new \RuntimeException('Handler failed'),
375+
);
376+
377+
$handlersLocator = new HandlersLocator([
378+
DummyMessage::class => [
379+
new HandlerDescriptor($successHandler, ['alias' => 'successHandler']),
380+
new HandlerDescriptor($failureHandler, ['alias' => 'failureHandler']),
381+
],
382+
]);
383+
384+
$successHandlerName = $successHandler::class.'::__invoke@successHandler';
385+
$failureHandlerName = $failureHandler::class.'::__invoke@failureHandler';
386+
387+
$dispatcher = $this->createMock(EventDispatcherInterface::class);
388+
389+
$handledStamp = new HandledStamp(null, $successHandlerName);
390+
391+
$dispatcher->expects($this->exactly(4))
392+
->method('dispatch')
393+
->withConsecutive(
394+
[new HandlerStartingEvent($envelope, $successHandlerName)],
395+
[new HandlerSuccessEvent($envelope->with($handledStamp), $successHandlerName)],
396+
[new HandlerStartingEvent($envelope->with($handledStamp), $failureHandlerName)],
397+
[new HandlerFailureEvent($envelope->with($handledStamp), $failureHandlerName, $exception)],
398+
);
399+
400+
$middleware = new HandleMessageMiddleware($handlersLocator, eventDispatcher: $dispatcher);
401+
402+
$this->expectException(HandlerFailedException::class);
403+
$middleware->handle($envelope, new StackMiddleware());
404+
}
359405
}
360406

361407
class HandleMessageMiddlewareTestCallable

0 commit comments

Comments
 (0)