8000 [Messenger] Be able to get raw data when a message in not decodable b… · symfony/symfony@4c0d36f · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c0d36f

Browse files
committed
[Messenger] Be able to get raw data when a message in not decodable by the PHP Serializer
8000
1 parent 19b6687 commit 4c0d36f

File tree

9 files changed

+263
-55
lines changed

9 files changed

+263
-55
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,20 +190,23 @@
190190
service('messenger.routable_message_bus'),
191191
service('event_dispatcher'),
192192
service('logger'),
193+
service('messenger.transport.native_php_serializer')->nullOnInvalid(),
193194
])
194195
->tag('console.command')
195196

196197
->set('console.command.messenger_failed_messages_show', FailedMessagesShowCommand::class)
197198
->args([
198199
abstract_arg('Default failure receiver name'),
199200
abstract_arg('Receivers'),
201+
service('messenger.transport.native_php_serializer')->nullOnInvalid(),
200202
])
201203
->tag('console.command')
202204

203205
->set('console.command.messenger_failed_messages_remove', FailedMessagesRemoveCommand::class)
204206
->args([
205207
abstract_arg('Default failure receiver name'),
206208
abstract_arg('Receivers'),
209+
service('messenger.transport.native_php_serializer')->nullOnInvalid(),
207210
])
208211
->tag('console.command')
209212

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
use Symfony\Component\Messenger\Envelope;
2222
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
2323
use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
24+
use Symfony\Component\Messenger\Stamp\MessageDecodingFailedStamp;
2425
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
2526
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
2627
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
2728
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
2829
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
2930
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
31+
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
3032
use Symfony\Component\VarDumper\Caster\Caster;
3133
use Symfony\Component\VarDumper\Caster\TraceStub;
3234
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
@@ -44,13 +46,15 @@ abstract class AbstractFailedMessagesCommand extends Command
4446
protected const DEFAULT_TRANSPORT_OPTION = 'choose';
4547

4648
protected $failureTransports;
49+
protected ?PhpSerializer $phpSerializer;
4750

4851
private ?string $globalFailureReceiverName;
4952

50-
public function __construct(?string $globalFailureReceiverName, ServiceProviderInterface $failureTransports)
53+
public function __construct(?string $globalFailureReceiverName, ServiceProviderInterface $failureTransports, PhpSerializer $phpSerializer = null)
5154
{
5255
$this->failureTransports = $failureTransports;
5356
$this->globalFailureReceiverName = $globalFailureReceiverName;
57+
$this->phpSerializer = $phpSerializer;
5458

5559
parent::__construct();
5660
}
@@ -78,6 +82,8 @@ protected function displaySingleMessage(Envelope F438 $envelope, SymfonyStyle $io)
7882
$lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
7983
/** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */
8084
$lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class);
85+
/** @var MessageDecodingFailedStamp|null $lastMessageDecodingFailedStamp */
86+
$lastMessageDecodingFailedStamp = $envelope->last(MessageDecodingFailedStamp::class);
8187

8288
$rows = [
8389
['Class', \get_class($envelope->getMessage())],
@@ -126,12 +132,18 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
126132

127133
if ($io->isVeryVerbose()) {
128134
$io->title('Message:');
135+
if (null !== $lastMessageDecodingFailedStamp) {
136+
$io->error('The message could not be decoded. See below an APPROXIMATIVE representation of the class.');
137+
}
129138
$dump = new Dumper($io, null, $this->createCloner());
130139
$io->writeln($dump($envelope->getMessage()));
131140
$io->title('Exception:');
132141
$flattenException = $lastErrorDetailsStamp?->getFlattenException();
133142
$io->writeln(null === $flattenException ? '(no data)' : $dump($flattenException));
134143
} else {
144+
if (null !== $lastMessageDecodingFailedStamp) {
145+
$io->error('The message could not be decoded.');
146+
}
135147
$io->writeln(' Re-run command with <info>-vv</info> to see more message & error details.');
136148
}
137149
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,13 @@ private function removeMessages(string $failureTransportName, array $ids, Receiv
8080
}
8181

8282
foreach ($ids as $id) {
83-
$envelope = $receiver->find($id);
83+
try {
84+
$this->phpSerializer?->enableClassNotFoundCreation();
85+
$envelope = $receiver->find($id);
86+
} finally {
87+
$this->phpSerializer?->enableClassNotFoundCreation(false);
88+
}
89+
8490
if (null === $envelope) {
8591
$io->error(sprintf('The message with id "%s" was not found.', $id));
8692
continue;

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

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2424
use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
2525
use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener;
26-
use Symfony\Component\Messenger\Exception\LogicException;
2726
use Symfony\Component\Messenger\MessageBusInterface;
27+
use Symfony\Component\Messenger\Stamp\MessageDecodingFailedStamp;
2828
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
2929
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
3030
use Symfony\Component\Messenger\Transport\Receiver\SingleMessageReceiver;
31+
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
3132
use Symfony\Component\Messenger\Worker;
3233
use Symfony\Contracts\Service\ServiceProviderInterface;
3334

@@ -41,13 +42,13 @@ class FailedMessagesRetryCommand extends AbstractFailedMessagesCommand
4142
private MessageBusInterface $messageBus;
4243
private ?LoggerInterface $logger;
4344

44-
public function __construct(?string $globalReceiverName, ServiceProviderInterface $failureTransports, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null)
45+
public function __construct(?string $globalReceiverName, ServiceProviderInterface $failureTransports, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null, PhpSerializer $phpSerializer = null)
4546
{
4647
$this->eventDispatcher = $eventDispatcher;
4748
$this->messageBus = $messageBus;
4849
$this->logger = $logger;
4950

50-
parent::__construct($globalReceiverName, $failureTransports);
51+
parent::__construct($globalReceiverName, $failureTransports, $phpSerializer);
5152
}
5253

5354
/**
@@ -139,23 +140,24 @@ private function runInteractive(string $failureTransportName, SymfonyStyle $io,
139140
// to be temporarily "acked", even if the user aborts
140141
// handling the message
141142
while (true) {
142-
$ids = [];
143-
foreach ($receiver->all(1) as $envelope) {
144-
++$count;
143+
$envelopes = [];
144+
try {
145+
$this->phpSerializer?->enableClassNotFoundCreation();
145146

146-
$id = $this->getMessageId($envelope);
147-
if (null === $id) {
148-
throw new LogicException(sprintf('The "%s" receiver is able to list messages by id but the envelope is missing the TransportMessageIdStamp stamp.', $failureTransportName));
147+
foreach ($receiver->all(1) as $envelope) {
148+
++$count;
149+
$envelopes[] = $envelope;
149150
}
150-
$ids[] = $id;
151+
} finally {
152+
$this->phpSerializer?->enableClassNotFoundCreation(false);
151153
}
152154

153155
// break the loop if all messages are consumed
154-
if (0 === \count($ids)) {
156+
if (0 === \count($envelopes)) {
155157
break;
156158
}
157159

158-
$this->retrySpecificIds($failureTransportName, $ids, $io, $shouldForce);
160+
$this->retrySpecificEnvelops($envelopes, $failureTransportName, $io, $shouldForce);
159161
}
160162
} else {
161163
// get() and ask messages one-by-one
@@ -177,6 +179,10 @@ private function runWorker(string $failureTransportName, ReceiverInterface $rece
177179

178180
$this->displaySingleMessage($envelope, $io);
179181

182+
if ($envelope->last(MessageDecodingFailedStamp::class)) {
183+
throw new \RuntimeException(sprintf('This message with id "%s" could not decoded. It can only be shown or removed.', $this->getMessageId($envelope) ?? 'NULL'));
184+
}
185+
180186
$shouldHandle = $shouldForce || $io->confirm('Do you want to retry (yes) or delete this message (no)?');
181187

182188
if ($shouldHandle) {
@@ -213,7 +219,13 @@ private function retrySpecificIds(string $failureTransportName, array $ids, Symf
213219
}
214220

215221
foreach ($ids as $id) {
216-
$envelope = $receiver->find($id);
222+
try {
223+
$this->phpSerializer?->enableClassNotFoundCreation();
224+
225+
$envelope = $receiver->find($id);
226+
} finally {
227+
$this->phpSerializer?->enableClassNotFoundCreation(false);
228+
}
217229
if (null === $envelope) {
218230
throw new RuntimeException(sprintf('The message "%s" was not found.', $id));
219231
}
@@ -222,4 +234,14 @@ private function retrySpecificIds(string $failureTransportName, array $ids, Symf
222234
$this->runWorker($failureTransportName, $singleReceiver, $io, $shouldForce);
223235
}
224236
}
237+
238+
private function retrySpecificEnvelops(array $envelopes, string $failureTransportName, SymfonyStyle $io, bool $shouldForce)
239+
{
240+
$receiver = $this->getReceiver($failureTransportName);
241+
242+
foreach ($envelopes as $envelope) {
243+
$singleReceiver = new SingleMessageReceiver($receiver, $envelope);
244+
$this->runWorker($failureTransportName, $singleReceiver, $io, $shouldForce);
245+
}
246+
}
225247
}

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

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,29 +102,35 @@ private function listMessages(?string $failedTransportName, SymfonyStyle $io, in
102102
$io->comment(sprintf('Displaying only \'%s\' messages', $classFilter));
103103
}
104104

105-
foreach ($envelopes as $envelope) {
106-
$currentClassName = \get_class($envelope->getMessage());
107-
108-
if ($classFilter && $classFilter !== $currentClassName) {
109-
continue;
110-
}
111-
112-
/** @var RedeliveryStamp|null $lastRedeliveryStamp */
113-
$lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
114-
/** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */
115-
$lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class);
116-
117-
$errorMessage = '';
118-
if (null !== $lastErrorDetailsStamp) {
119-
$errorMessage = $lastErrorDetailsStamp->getExceptionMessage();
105+
try {
106+
$this->phpSerializer?->enableClassNotFoundCreation();
107+
108+
foreach ($envelopes as $envelope) {
109+
$currentClassName = \get_class($envelope->getMessage());
110+
111+
if ($classFilter && $classFilter !== $currentClassName) {
112+
continue;
113+
}
114+
115+
/** @var RedeliveryStamp|null $lastRedeliveryStamp */
116+
$lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
117+
/** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */
118+
$lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class);
119+
120+
$errorMessage = '';
121+
if (null !== $lastErrorDetailsStamp) {
122+
$errorMessage = $lastErrorDetailsStamp->getExceptionMessage();
123+
}
124+
125+
$rows[] = [
126+
$this->getMessageId($envelope),
127+
$currentClassName,
128+
null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'),
129+
$errorMessage,
130+
];
120131
}
121-
122-
$rows[] = [
123-
$this->getMessageId($envelope),
124-
$currentClassName,
125-
null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'),
126-
$errorMessage,
127-
];
132+
} finally {
133+
$this->phpSerializer?->enableClassNotFoundCreation(false);
128134
}
129135

130136
$rowsCount = \count($rows);
@@ -154,14 +160,19 @@ private function listMessagesPerClass(?string $failedTransportName, SymfonyStyle
154160

155161
$countPerClass = [];
156162

157-
foreach ($envelopes as $envelope) {
158-
$c = \get_class($envelope->getMessage());
163+
try {
164+
$this->phpSerializer?->enableClassNotFoundCreation();
165+
foreach ($envelopes as $envelope) {
166+
$c = \get_class($envelope->getMessage());
159167

160-
if (!isset($countPerClass[$c])) {
161-
$countPerClass[$c] = [$c, 0];
162-
}
168+
if (!isset($countPerClass[$c])) {
169+
$countPerClass[$c] = [$c, 0];
170+
}
163171

164-
++$countPerClass[$c][1];
172+
++$countPerClass[$c][1];
173+
}
174+
} finally {
175+
$this->phpSerializer?->enableClassNotFoundCreation(false);
165176
}
166177

167178
if (0 === \count($countPerClass)) {
@@ -177,7 +188,12 @@ private function showMessage(?string $failedTransportName, string $id, SymfonySt
177188
{
178189
/** @var ListableReceiverInterface $receiver */
179190
$receiver = $this->getReceiver($failedTransportName);
180-
$envelope = $receiver->find($id);
191+
try {
192+
$this->phpSerializer?->enableClassNotFoundCreation();
193+
$envelope = $receiver->find($id);
194+
} finally {
195+
$this->phpSerializer?->enableClassNotFoundCreation(false);
196+
}
181197
if (null === $envelope) {
182198
throw new RuntimeException(sprintf('The message "%s" was not found.', $id));
183199
}
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\Stamp;
13+
14+
/**
15+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
16+
*/
17+
class MessageDecodingFailedStamp implements StampInterface
18+
{
19+
}

0 commit comments

Comments
 (0)
0