Skip to content

Commit 363e217

Browse files
committed
feature #51653 [Messenger] Add WrappedExceptionsInterface for nested exceptions (Jeroeny)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [Messenger] Add WrappedExceptionsInterface for nested exceptions | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | yes | License | MIT Metric & logging tools often want to measure / log individual exceptions from the messenger. There are currently two exception classes that hold a bunch of collected exceptions from different messages or handlers. It would be nice if there was a single interface to check and call upon when extracting these nested exceptions. Example usecase: https://github.com/getsentry/sentry-symfony/pull/760/files#diff-da0fb4498178e4866e794b813999618022c327dea59f9277b86a7abf784aeafaR98 Commits ------- a3fe850 [Messenger] Add WrappedExceptionsInterface for nested exceptions
2 parents decb566 + a3fe850 commit 363e217

12 files changed

+148
-12
lines changed

UPGRADE-6.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ Messenger
113113
---------
114114

115115
* Deprecate `StopWorkerOnSignalsListener` in favor of using the `SignalableCommandInterface`
116+
* Deprecate `HandlerFailedException::getNestedExceptions()`, `HandlerFailedException::getNestedExceptionsOfClass()` and `DelayedMessageHandlingException::getExceptions()` which are replaced by a new `getWrappedExceptions()` method
116117

117118
MonologBridge
118119
-------------

src/Symfony/Bridge/Doctrine/Messenger/DoctrineTransactionMiddleware.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected function handleForManager(EntityManagerInterface $entityManager, Envel
3939
if ($exception instanceof HandlerFailedException) {
4040
// Remove all HandledStamp from the envelope so the retry will execute all handlers again.
4141
// When a handler fails, the queries of allegedly successful previous handlers just got rolled back.
42-
throw new HandlerFailedException($exception->getEnvelope()->withoutAll(HandledStamp::class), $exception->getNestedExceptions());
42+
throw new HandlerFailedException($exception->getEnvelope()->withoutAll(HandledStamp::class), $exception->getWrappedExceptions());
4343
}
4444

4545
throw $exception;

src/Symfony/Component/Mailer/Mailer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function send(RawMessage $message, Envelope $envelope = null): void
6565
try {
6666
$this->bus->dispatch(new SendEmailMessage($message, $envelope), $stamps);
6767
} catch (HandlerFailedException $e) {
68-
foreach ($e->getNestedExceptions() as $nested) {
68+
foreach ($e->getWrappedExceptions() as $nested) {
6969
if ($nested instanceof TransportExceptionInterface) {
7070
throw $nested;
7171
}

src/Symfony/Component/Messenger/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ CHANGELOG
99
* Add support for multiple Redis Sentinel hosts
1010
* Add `--all` option to the `messenger:failed:remove` command
1111
* `RejectRedeliveredMessageException` implements `UnrecoverableExceptionInterface` in order to not be retried
12+
* Add `WrappedExceptionsInterface` interface for exceptions that hold multiple individual exceptions
13+
* Deprecate `HandlerFailedException::getNestedExceptions()`, `HandlerFailedException::getNestedExceptionsOfClass()`
14+
and `DelayedMessageHandlingException::getExceptions()` which are replaced by a new `getWrappedExceptions()` method
1215

1316
6.3
1417
---

src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInt
128128
// if ALL nested Exceptions are an instance of UnrecoverableExceptionInterface we should not retry
129129
if ($e instanceof HandlerFailedException) {
130130
$shouldNotRetry = true;
131-
foreach ($e->getNestedExceptions() as $nestedException) {
131+
foreach ($e->getWrappedExceptions() as $nestedException) {
132132
if ($nestedException instanceof RecoverableExceptionInterface) {
133133
return true;
134134
}

src/Symfony/Component/Messenger/EventListener/StopWorkerOnCustomStopExceptionListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function onMessageFailed(WorkerMessageFailedEvent $event): void
3131
$this->stop = true;
3232
}
3333
if ($th instanceof HandlerFailedException) {
34-
foreach ($th->getNestedExceptions() as $e) {
34+
foreach ($th->getWrappedExceptions() as $e) {
3535
if ($e instanceof StopWorkerExceptionInterface) {
3636
$this->stop = true;
3737
break;

src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
*
2020
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
2121
*/
22-
class DelayedMessageHandlingException extends RuntimeException
22+
class DelayedMessageHandlingException extends RuntimeException implements WrappedExceptionsInterface
2323
{
24+
use WrappedExceptionsTrait;
25+
2426
private array $exceptions;
2527
private Envelope $envelope;
2628

@@ -41,11 +43,16 @@ public function __construct(array $exceptions, Envelope $envelope)
4143

4244
$this->exceptions = $exceptions;
4345

44-
parent::__construct($message, 0, $exceptions[0]);
46+
parent::__construct($message, 0, $exceptions[array_key_first($exceptions)]);
4547
}
4648

49+
/**
50+
* @deprecated since Symfony 6.4, use {@see self::getWrappedExceptions()} instead
51+
*/
4752
public function getExceptions(): array
4853
{
54+
trigger_deprecation('symfony/messenger', '6.4', 'The "%s()" method is deprecated, use "%s::getWrappedExceptions" instead.', __METHOD__, self::class);
55+
4956
return $this->exceptions;
5057
}
5158

src/Symfony/Component/Messenger/Exception/HandlerFailedException.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
use Symfony\Component\Messenger\Envelope;
1515

16-
class HandlerFailedException extends RuntimeException
16+
class HandlerFailedException extends RuntimeException implements WrappedExceptionsInterface
1717
{
18+
use WrappedExceptionsTrait;
19+
1820
private array $exceptions;
1921
private Envelope $envelope;
2022

@@ -46,15 +48,24 @@ public function getEnvelope(): Envelope
4648
}
4749

4850
/**
51+
* @deprecated since Symfony 6.4, use {@see self::getWrappedExceptions()} instead
52+
*
4953
* @return \Throwable[]
5054
*/
5155
public function getNestedExceptions(): array
5256
{
57+
trigger_deprecation('symfony/messenger', '6.4', 'The "%s()" method is deprecated, use "%s::getWrappedExceptions" instead.', __METHOD__, self::class);
58+
5359
return $this->exceptions;
5460
}
5561

62+
/**
63+
* @deprecated since Symfony 6.4, use {@see self::getWrappedExceptions()} instead
64+
*/
5665
public function getNestedExceptionOfClass(string $exceptionClassName): array
5766
{
67+
trigger_deprecation('symfony/messenger', '6.4', 'The "%s()" method is deprecated, use "%s::getWrappedExceptions" instead.', __METHOD__, self::class);
68+
5869
return array_values(
5970
array_filter(
6071
$this->exceptions,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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\Exception;
13+
14+
/**
15+
* Exception that holds multiple exceptions thrown by one or more handlers and/or messages.
16+
*
17+
* @author Jeroen <https://github.com/Jeroeny>
18+
*/
19+
interface WrappedExceptionsInterface
20+
{
21+
/**
22+
* @return \Throwable[]
23+
*/
24+
public function getWrappedExceptions(string $class = null, bool $recursive = false): array;
25+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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\Exception;
13+
14+
/**
15+
* @author Jeroen <https://github.com/Jeroeny>
16+
*
17+
* @internal
18+
*/
19+
trait WrappedExceptionsTrait
20+
{
21+
/**
22+
* @return \Throwable[]
23+
*/
24+
public function getWrappedExceptions(string $class = null, bool $recursive = false): array
25+
{
26+
return $this->getWrappedExceptionsRecursively($class, $recursive, $this->exceptions);
27+
}
28+
29+
/**
30+
* @param class-string<\Throwable>|null $class
31+
* @param iterable<\Throwable> $exceptions
32+
*
33+
* @return \Throwable[]
34+
*/
35+
private function getWrappedExceptionsRecursively(?string $class, bool $recursive, iterable $exceptions): array
36+
{
37+
$unwrapped = [];
38+
foreach ($exceptions as $key => $exception) {
39+
if ($recursive && $exception instanceof WrappedExceptionsInterface) {
40+
$unwrapped[] = $this->getWrappedExceptionsRecursively($class, $recursive, $exception->getWrappedExceptions());
41+
42+
continue;
43+
}
44+
45+
if ($class && !is_a($exception, $class)) {
46+
continue;
47+
}
48+
49+
$unwrapped[] = [$key => $exception];
50+
}
51+
52+
return array_merge(...$unwrapped);
53+
}
54+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy