feature #31471 [Messenger] Add "non sendable" stamps (weaverryan)
This PR was merged into the 4.3 branch.
Discussion
----------
[Messenger] Add "non sendable" stamps
| Q | A
| ------------- | ---
| Branch? | 4.3
| Bug fix? | yes
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #31460
| License | MIT
| Doc PR | not needed
Fixes a bug where Symfony serialization of the AmqpReceivedStamp sometimes caused problems.
It's still a mystery why the `AmqpReceivedStamp` caused a segfault *sometimes* when going through the Symfony serializer or the `VarDumper`. But, that stamp really didn't need to be sent on redelivery anyways.
I don't love making the removal the responsibility of the serializers, but it didn't work well anywhere else.
Cheers!
Commits
-------
34e7781c5c
Adding a new NonSendableStampInterface to avoid sending certain stamps
This commit is contained in:
commit
65458059d8
@ -4,6 +4,9 @@ CHANGELOG
|
|||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* Added `NonSendableStampInterface` that a stamp can implement if
|
||||||
|
it should not be sent to a transport. Transport serializers
|
||||||
|
must now check for these stamps and not encode them.
|
||||||
* [BC BREAK] `SendersLocatorInterface` has an additional method:
|
* [BC BREAK] `SendersLocatorInterface` has an additional method:
|
||||||
`getSenderByAlias()`.
|
`getSenderByAlias()`.
|
||||||
* Removed argument `?bool &$handle = false` from `SendersLocatorInterface::getSenders`
|
* Removed argument `?bool &$handle = false` from `SendersLocatorInterface::getSenders`
|
||||||
|
@ -80,6 +80,22 @@ final class Envelope
|
|||||||
return $cloned;
|
return $cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all stamps that implement the given type.
|
||||||
|
*/
|
||||||
|
public function withoutStampsOfType(string $type): self
|
||||||
|
{
|
||||||
|
$cloned = clone $this;
|
||||||
|
|
||||||
|
foreach ($cloned->stamps as $class => $stamps) {
|
||||||
|
if ($class === $type || \is_subclass_of($class, $type)) {
|
||||||
|
unset($cloned->stamps[$class]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cloned;
|
||||||
|
}
|
||||||
|
|
||||||
public function last(string $stampFqcn): ?StampInterface
|
public function last(string $stampFqcn): ?StampInterface
|
||||||
{
|
{
|
||||||
return isset($this->stamps[$stampFqcn]) ? end($this->stamps[$stampFqcn]) : null;
|
return isset($this->stamps[$stampFqcn]) ? end($this->stamps[$stampFqcn]) : null;
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Messenger\Stamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stamp that should not be included with the Envelope if sent to a transport.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||||
|
*
|
||||||
|
* @experimental in 4.3
|
||||||
|
*/
|
||||||
|
interface NonSendableStampInterface extends StampInterface
|
||||||
|
{
|
||||||
|
}
|
@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Stamp\DelayStamp;
|
use Symfony\Component\Messenger\Stamp\DelayStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
|
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
|
||||||
|
use Symfony\Component\Messenger\Stamp\StampInterface;
|
||||||
use Symfony\Component\Messenger\Stamp\ValidationStamp;
|
use Symfony\Component\Messenger\Stamp\ValidationStamp;
|
||||||
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
||||||
|
|
||||||
@ -50,6 +51,30 @@ class EnvelopeTest extends TestCase
|
|||||||
$this->assertCount(1, $envelope->all(DelayStamp::class));
|
$this->assertCount(1, $envelope->all(DelayStamp::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWithoutStampsOfType()
|
||||||
|
{
|
||||||
|
$envelope = new Envelope(new DummyMessage('dummy'), [
|
||||||
|
new ReceivedStamp('transport1'),
|
||||||
|
new DelayStamp(5000),
|
||||||
|
new DummyExtendsDelayStamp(5000),
|
||||||
|
new DummyImplementsFooBarStampInterface(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$envelope2 = $envelope->withoutStampsOfType(DummyNothingImplementsMeStampInterface::class);
|
||||||
|
$this->assertEquals($envelope, $envelope2);
|
||||||
|
|
||||||
|
$envelope3 = $envelope2->withoutStampsOfType(ReceivedStamp::class);
|
||||||
|
$this->assertEmpty($envelope3->all(ReceivedStamp::class));
|
||||||
|
|
||||||
|
$envelope4 = $envelope3->withoutStampsOfType(DelayStamp::class);
|
||||||
|
$this->assertEmpty($envelope4->all(DelayStamp::class));
|
||||||
|
$this->assertEmpty($envelope4->all(DummyExtendsDelayStamp::class));
|
||||||
|
|
||||||
|
$envelope5 = $envelope4->withoutStampsOfType(DummyFooBarStampInterface::class);
|
||||||
|
$this->assertEmpty($envelope5->all(DummyImplementsFooBarStampInterface::class));
|
||||||
|
$this->assertEmpty($envelope5->all());
|
||||||
|
}
|
||||||
|
|
||||||
public function testLast()
|
public function testLast()
|
||||||
{
|
{
|
||||||
$receivedStamp = new ReceivedStamp('transport');
|
$receivedStamp = new ReceivedStamp('transport');
|
||||||
@ -92,3 +117,16 @@ class EnvelopeTest extends TestCase
|
|||||||
$this->assertCount(1, $envelope->all(ReceivedStamp::class));
|
$this->assertCount(1, $envelope->all(ReceivedStamp::class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DummyExtendsDelayStamp extends DelayStamp
|
||||||
|
{
|
||||||
|
}
|
||||||
|
interface DummyFooBarStampInterface extends StampInterface
|
||||||
|
{
|
||||||
|
}
|
||||||
|
interface DummyNothingImplementsMeStampInterface extends StampInterface
|
||||||
|
{
|
||||||
|
}
|
||||||
|
class DummyImplementsFooBarStampInterface implements DummyFooBarStampInterface
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\Serialization;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
||||||
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
||||||
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
|
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
|
||||||
|
|
||||||
@ -63,4 +64,20 @@ class PhpSerializerTest extends TestCase
|
|||||||
'body' => 'O:13:"ReceivedSt0mp":0:{}',
|
'body' => 'O:13:"ReceivedSt0mp":0:{}',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEncodedSkipsNonEncodeableStamps()
|
||||||
|
{
|
||||||
|
$serializer = new PhpSerializer();
|
||||||
|
|
||||||
|
$envelope = new Envelope(new DummyMessage('Hello'), [
|
||||||
|
new DummyPhpSerializerNonSendableStamp(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$encoded = $serializer->encode($envelope);
|
||||||
|
$this->assertNotContains('DummyPhpSerializerNonSendableStamp', $encoded['body']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyPhpSerializerNonSendableStamp implements NonSendableStampInterface
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Messenger\Tests\Transport\Serialization;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
||||||
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
use Symfony\Component\Messenger\Stamp\SerializerStamp;
|
use Symfony\Component\Messenger\Stamp\SerializerStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\ValidationStamp;
|
use Symfony\Component\Messenger\Stamp\ValidationStamp;
|
||||||
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
|
||||||
@ -193,4 +194,19 @@ class SerializerTest extends TestCase
|
|||||||
'headers' => ['type' => 'NonExistentClass'],
|
'headers' => ['type' => 'NonExistentClass'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEncodedSkipsNonEncodeableStamps()
|
||||||
|
{
|
||||||
|
$serializer = new Serializer();
|
||||||
|
|
||||||
|
$envelope = new Envelope(new DummyMessage('Hello'), [
|
||||||
|
new DummySymfonySerializerNonSendableStamp(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$encoded = $serializer->encode($envelope);
|
||||||
|
$this->assertNotContains('DummySymfonySerializerNonSendableStamp', print_r($encoded['headers'], true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class DummySymfonySerializerNonSendableStamp implements NonSendableStampInterface
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Messenger\Transport\AmqpExt;
|
namespace Symfony\Component\Messenger\Transport\AmqpExt;
|
||||||
|
|
||||||
use Symfony\Component\Messenger\Stamp\StampInterface;
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stamp applied when a message is received from Amqp.
|
* Stamp applied when a message is received from Amqp.
|
||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class AmqpReceivedStamp implements StampInterface
|
class AmqpReceivedStamp implements NonSendableStampInterface
|
||||||
{
|
{
|
||||||
private $amqpEnvelope;
|
private $amqpEnvelope;
|
||||||
private $queueName;
|
private $queueName;
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Messenger\Transport\Doctrine;
|
namespace Symfony\Component\Messenger\Transport\Doctrine;
|
||||||
|
|
||||||
use Symfony\Component\Messenger\Stamp\StampInterface;
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vincent Touzet <vincent.touzet@gmail.com>
|
* @author Vincent Touzet <vincent.touzet@gmail.com>
|
||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class DoctrineReceivedStamp implements StampInterface
|
class DoctrineReceivedStamp implements NonSendableStampInterface
|
||||||
{
|
{
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Messenger\Transport\RedisExt;
|
namespace Symfony\Component\Messenger\Transport\RedisExt;
|
||||||
|
|
||||||
use Symfony\Component\Messenger\Stamp\StampInterface;
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Schranz <alexander@sulu.io>
|
* @author Alexander Schranz <alexander@sulu.io>
|
||||||
*
|
*
|
||||||
* @experimental in 4.3
|
* @experimental in 4.3
|
||||||
*/
|
*/
|
||||||
class RedisReceivedStamp implements StampInterface
|
class RedisReceivedStamp implements NonSendableStampInterface
|
||||||
{
|
{
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Messenger\Transport\Serialization;
|
|||||||
|
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
||||||
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Weaver<ryan@symfonycasts.com>
|
* @author Ryan Weaver<ryan@symfonycasts.com>
|
||||||
@ -40,6 +41,8 @@ class PhpSerializer implements SerializerInterface
|
|||||||
*/
|
*/
|
||||||
public function encode(Envelope $envelope): array
|
public function encode(Envelope $envelope): array
|
||||||
{
|
{
|
||||||
|
$envelope = $envelope->withoutStampsOfType(NonSendableStampInterface::class);
|
||||||
|
|
||||||
$body = addslashes(serialize($envelope));
|
$body = addslashes(serialize($envelope));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Messenger\Transport\Serialization;
|
|||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Exception\LogicException;
|
use Symfony\Component\Messenger\Exception\LogicException;
|
||||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
|
||||||
|
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
|
||||||
use Symfony\Component\Messenger\Stamp\SerializerStamp;
|
use Symfony\Component\Messenger\Stamp\SerializerStamp;
|
||||||
use Symfony\Component\Messenger\Stamp\StampInterface;
|
use Symfony\Component\Messenger\Stamp\StampInterface;
|
||||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||||
@ -98,6 +99,8 @@ class Serializer implements SerializerInterface
|
|||||||
$context = $serializerStamp->getContext() + $context;
|
$context = $serializerStamp->getContext() + $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$envelope = $envelope->withoutStampsOfType(NonSendableStampInterface::class);
|
||||||
|
|
||||||
$headers = ['type' => \get_class($envelope->getMessage())] + $this->encodeStamps($envelope);
|
$headers = ['type' => \get_class($envelope->getMessage())] + $this->encodeStamps($envelope);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -39,6 +39,9 @@ interface SerializerInterface
|
|||||||
* Encodes an envelope content (message & stamps) to a common format understandable by transports.
|
* Encodes an envelope content (message & stamps) to a common format understandable by transports.
|
||||||
* The encoded array should only contain scalars and arrays.
|
* The encoded array should only contain scalars and arrays.
|
||||||
*
|
*
|
||||||
|
* Stamps that implement NonSendableStampInterface should
|
||||||
|
* not be encoded.
|
||||||
|
*
|
||||||
* The most common keys of the encoded array are:
|
* The most common keys of the encoded array are:
|
||||||
* - `body` (string) - the message body
|
* - `body` (string) - the message body
|
||||||
* - `headers` (string<string>) - a key/value pair of headers
|
* - `headers` (string<string>) - a key/value pair of headers
|
||||||
|
Reference in New Issue
Block a user