From c31a686e067b3b58203cd8aec16b8050c8e8b32d Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 14 May 2026 15:18:41 +0300 Subject: [PATCH 1/4] refactor --- src/Message/DelayEnvelope.php | 11 ++-- src/Message/Envelope.php | 53 +++++++++++-------- src/Message/IdEnvelope.php | 15 ++---- .../FailureHandling/FailureEnvelope.php | 18 +++---- tests/App/DummyEnvelope.php | 8 +-- 5 files changed, 48 insertions(+), 57 deletions(-) diff --git a/src/Message/DelayEnvelope.php b/src/Message/DelayEnvelope.php index 8f79c85c..52aaec84 100644 --- a/src/Message/DelayEnvelope.php +++ b/src/Message/DelayEnvelope.php @@ -8,9 +8,9 @@ final class DelayEnvelope extends Envelope { public const META_DELAY_SECONDS = 'yii-delay'; - public function __construct(MessageInterface $message, private readonly float $delaySeconds) + public function __construct(MessageInterface $message, float $delaySeconds) { - parent::__construct($message); + parent::__construct($message, [self::META_DELAY_SECONDS => $delaySeconds]); } public static function fromMessage(MessageInterface $message): static @@ -22,11 +22,6 @@ public static function fromMessage(MessageInterface $message): static public function getDelaySeconds(): float { - return $this->delaySeconds; - } - - protected function getEnvelopeMetadata(): array - { - return [self::META_DELAY_SECONDS => $this->delaySeconds]; + return $this->getMetadata()[self::META_DELAY_SECONDS] ?? 0.0; } } diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 800ebae2..97e9fc8a 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -8,12 +8,22 @@ abstract class Envelope implements EnvelopeInterface { + private readonly MessageInterface $message; + /** - * @psalm-var array|null + * @psalm-var array */ - private ?array $metadata = null; + private readonly array $metadata; + + public function __construct(MessageInterface $message, array $metadata) + { + $this->metadata = $this->prepareMetadata($message->getMetadata(), $metadata); - public function __construct(protected MessageInterface $message) {} + while ($message instanceof self) { + $message = $message->getMessage(); + } + $this->message = $message; + } /** @psalm-suppress MoreSpecificReturnType */ public static function fromData(string $type, mixed $data, array $metadata = []): static @@ -39,28 +49,25 @@ public function getData(): mixed public function getMetadata(): array { - if ($this->metadata === null) { - $messageMeta = $this->message->getMetadata(); - - $stack = $messageMeta[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? []; - if (!is_array($stack)) { - $stack = []; - } + return $this->metadata; + } - $this->metadata = array_merge( - $messageMeta, - [ - EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( - $stack, - [static::class], - ), - ], - $this->getEnvelopeMetadata(), - ); + private function prepareMetadata(array $messageMeta, array $metadata): array + { + $stack = $messageMeta[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? []; + if (!is_array($stack)) { + $stack = []; } - return $this->metadata; + return array_merge( + $messageMeta, + [ + EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( + $stack, + [static::class], + ), + ], + $metadata, + ); } - - abstract protected function getEnvelopeMetadata(): array; } diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 891f9883..983eb08c 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -15,10 +15,10 @@ final class IdEnvelope extends Envelope { public const MESSAGE_ID_KEY = 'yii-message-id'; - public function __construct( - protected MessageInterface $message, - private readonly string|int|null $id, - ) {} + public function __construct(MessageInterface $message, string|int|null $id) + { + parent::__construct($message, [self::MESSAGE_ID_KEY => $id]); + } public static function fromMessage(MessageInterface $message): static { @@ -37,11 +37,6 @@ public static function fromMessage(MessageInterface $message): static public function getId(): string|int|null { - return $this->id; - } - - protected function getEnvelopeMetadata(): array - { - return [self::MESSAGE_ID_KEY => $this->getId()]; + return $this->getMetadata()[self::MESSAGE_ID_KEY]; } } diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 05e38870..56075a86 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -12,10 +12,12 @@ final class FailureEnvelope extends Envelope { public const FAILURE_META_KEY = 'failure-meta'; - public function __construct( - protected MessageInterface $message, - private readonly array $metadata = [], - ) {} + public function __construct(MessageInterface $message, array $metadata = []) + { + parent::__construct($message, [ + self::FAILURE_META_KEY => ArrayHelper::merge($message->getMetadata()[self::FAILURE_META_KEY] ?? [], $metadata), + ]); + } public static function fromMessage(MessageInterface $message): static { @@ -24,12 +26,4 @@ public static function fromMessage(MessageInterface $message): static return new self($message, $metadata); } - - protected function getEnvelopeMetadata(): array - { - /** @var array $metadata */ - $metadata = $this->message->getMetadata()[self::FAILURE_META_KEY] ?? []; - - return [self::FAILURE_META_KEY => ArrayHelper::merge($metadata, $this->metadata)]; - } } diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php index dc1d1f7d..e92fbeec 100644 --- a/tests/App/DummyEnvelope.php +++ b/tests/App/DummyEnvelope.php @@ -9,13 +9,13 @@ final class DummyEnvelope extends Envelope { - public static function fromMessage(MessageInterface $message): static + public function __construct(MessageInterface $message) { - return new self($message); + parent::__construct($message, []); } - protected function getEnvelopeMetadata(): array + public static function fromMessage(MessageInterface $message): static { - return []; + return new self($message); } } From 91456c1a8718b6750d379281b963db82a8588234 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 14 May 2026 15:25:32 +0300 Subject: [PATCH 2/4] remove `EnvelopeInterface` --- src/Message/Envelope.php | 23 +++++++++++-------- src/Message/EnvelopeInterface.php | 18 --------------- src/Message/JsonMessageSerializer.php | 10 ++++---- tests/Unit/EnvelopeTest.php | 6 ++--- tests/Unit/Message/EnvelopeTest.php | 8 +++---- .../Message/JsonMessageSerializerTest.php | 14 +++++------ 6 files changed, 32 insertions(+), 47 deletions(-) delete mode 100644 src/Message/EnvelopeInterface.php diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 97e9fc8a..1e3f1544 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -6,8 +6,11 @@ use function is_array; -abstract class Envelope implements EnvelopeInterface +abstract class Envelope implements MessageInterface { + /** @psalm-suppress MissingClassConstType */ + final public const ENVELOPE_STACK_KEY = 'envelopes'; + private readonly MessageInterface $message; /** @@ -25,36 +28,36 @@ public function __construct(MessageInterface $message, array $metadata) $this->message = $message; } - /** @psalm-suppress MoreSpecificReturnType */ - public static function fromData(string $type, mixed $data, array $metadata = []): static + final public static function fromData(string $type, mixed $data, array $metadata = []): static { - /** @psalm-suppress LessSpecificReturnStatement */ return static::fromMessage(Message::fromData($type, $data, $metadata)); } - public function getMessage(): MessageInterface + abstract public static function fromMessage(MessageInterface $message): static; + + final public function getMessage(): MessageInterface { return $this->message; } - public function getType(): string + final public function getType(): string { return $this->message->getType(); } - public function getData(): mixed + final public function getData(): mixed { return $this->message->getData(); } - public function getMetadata(): array + final public function getMetadata(): array { return $this->metadata; } private function prepareMetadata(array $messageMeta, array $metadata): array { - $stack = $messageMeta[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? []; + $stack = $messageMeta[self::ENVELOPE_STACK_KEY] ?? []; if (!is_array($stack)) { $stack = []; } @@ -62,7 +65,7 @@ private function prepareMetadata(array $messageMeta, array $metadata): array return array_merge( $messageMeta, [ - EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( + self::ENVELOPE_STACK_KEY => array_merge( $stack, [static::class], ), diff --git a/src/Message/EnvelopeInterface.php b/src/Message/EnvelopeInterface.php deleted file mode 100644 index 612fe368..00000000 --- a/src/Message/EnvelopeInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - $message->getMetadata(), ]; if (!isset($payload['meta']['message-class'])) { - $payload['meta']['message-class'] = $message instanceof EnvelopeInterface + $payload['meta']['message-class'] = $message instanceof Envelope ? $message->getMessage()::class : $message::class; } @@ -55,10 +55,10 @@ public function unserialize(string $value): MessageInterface } $envelopes = []; - if (isset($meta[EnvelopeInterface::ENVELOPE_STACK_KEY]) && is_array($meta[EnvelopeInterface::ENVELOPE_STACK_KEY])) { - $envelopes = $meta[EnvelopeInterface::ENVELOPE_STACK_KEY]; + if (isset($meta[Envelope::ENVELOPE_STACK_KEY]) && is_array($meta[Envelope::ENVELOPE_STACK_KEY])) { + $envelopes = $meta[Envelope::ENVELOPE_STACK_KEY]; } - $meta[EnvelopeInterface::ENVELOPE_STACK_KEY] = []; + $meta[Envelope::ENVELOPE_STACK_KEY] = []; $class = $meta['message-class'] ?? Message::class; // Don't check subclasses when it's a default class: that's faster @@ -72,7 +72,7 @@ public function unserialize(string $value): MessageInterface $message = $class::fromData($type, $payload['data'] ?? null, $meta); foreach ($envelopes as $envelope) { - if (is_string($envelope) && class_exists($envelope) && is_subclass_of($envelope, EnvelopeInterface::class)) { + if (is_string($envelope) && class_exists($envelope) && is_subclass_of($envelope, Envelope::class)) { $message = $envelope::fromMessage($message); } } diff --git a/tests/Unit/EnvelopeTest.php b/tests/Unit/EnvelopeTest.php index ad86abd5..5c9bc112 100644 --- a/tests/Unit/EnvelopeTest.php +++ b/tests/Unit/EnvelopeTest.php @@ -5,7 +5,7 @@ namespace Yiisoft\Queue\Tests\Unit; use PHPUnit\Framework\TestCase; -use Yiisoft\Queue\Message\EnvelopeInterface; +use Yiisoft\Queue\Message\Envelope; use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\Message; @@ -18,7 +18,7 @@ public function testEnvelopeStack(): void $this->assertEquals('test', $message->getMessage()->getData()); - $stack = $message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY]; + $stack = $message->getMetadata()[Envelope::ENVELOPE_STACK_KEY]; $this->assertIsArray($stack); $this->assertEquals([ @@ -37,7 +37,7 @@ public function testEnvelopeDuplicates(): void $this->assertEquals('test', $message->getMessage()->getData()); - $stack = $message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY]; + $stack = $message->getMetadata()[Envelope::ENVELOPE_STACK_KEY]; $this->assertIsArray($stack); $this->assertEquals([ IdEnvelope::class, diff --git a/tests/Unit/Message/EnvelopeTest.php b/tests/Unit/Message/EnvelopeTest.php index 93fdefce..22f109cd 100644 --- a/tests/Unit/Message/EnvelopeTest.php +++ b/tests/Unit/Message/EnvelopeTest.php @@ -5,8 +5,8 @@ namespace Yiisoft\Queue\Tests\Unit\Message; use PHPUnit\Framework\TestCase; +use Yiisoft\Queue\Message\Envelope; use Yiisoft\Queue\Tests\App\DummyEnvelope; -use Yiisoft\Queue\Message\EnvelopeInterface; use Yiisoft\Queue\Message\Message; final class EnvelopeTest extends TestCase @@ -28,11 +28,11 @@ public function testFromData(): void public function testNonArrayStackIsNormalized(): void { - $base = new Message('handler', 'data', [EnvelopeInterface::ENVELOPE_STACK_KEY => 'oops']); + $base = new Message('handler', 'data', [Envelope::ENVELOPE_STACK_KEY => 'oops']); $wrapped = new DummyEnvelope($base); $meta = $wrapped->getMetadata(); - self::assertIsArray($meta[EnvelopeInterface::ENVELOPE_STACK_KEY]); - self::assertSame([DummyEnvelope::class], $meta[EnvelopeInterface::ENVELOPE_STACK_KEY]); + self::assertIsArray($meta[Envelope::ENVELOPE_STACK_KEY]); + self::assertSame([DummyEnvelope::class], $meta[Envelope::ENVELOPE_STACK_KEY]); } } diff --git a/tests/Unit/Message/JsonMessageSerializerTest.php b/tests/Unit/Message/JsonMessageSerializerTest.php index ed511681..44f98281 100644 --- a/tests/Unit/Message/JsonMessageSerializerTest.php +++ b/tests/Unit/Message/JsonMessageSerializerTest.php @@ -7,7 +7,7 @@ use InvalidArgumentException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Yiisoft\Queue\Message\EnvelopeInterface; +use Yiisoft\Queue\Message\Envelope; use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Message\Message; @@ -115,7 +115,7 @@ public function testUnserializeFromData(): void $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); - $this->assertEquals([EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata()); + $this->assertEquals([Envelope::ENVELOPE_STACK_KEY => []], $message->getMetadata()); } public function testUnserializeWithMetadata(): void @@ -126,7 +126,7 @@ public function testUnserializeWithMetadata(): void $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); - $this->assertEquals(['int' => 1, 'str' => 'string', 'bool' => true, EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata()); + $this->assertEquals(['int' => 1, 'str' => 'string', 'bool' => true, Envelope::ENVELOPE_STACK_KEY => []], $message->getMetadata()); } public function testUnserializeEnvelopeStack(): void @@ -135,7 +135,7 @@ public function testUnserializeEnvelopeStack(): void 'type' => 'handler', 'data' => 'test', 'meta' => [ - EnvelopeInterface::ENVELOPE_STACK_KEY => [ + Envelope::ENVELOPE_STACK_KEY => [ IdEnvelope::class, ], ], @@ -146,7 +146,7 @@ public function testUnserializeEnvelopeStack(): void $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); - $this->assertEquals([IdEnvelope::class], $message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY]); + $this->assertEquals([IdEnvelope::class], $message->getMetadata()[Envelope::ENVELOPE_STACK_KEY]); $this->assertInstanceOf(IdEnvelope::class, $message); $this->assertNull($message->getId()); @@ -192,7 +192,7 @@ public function testSerializeEnvelopeStack(): void $this->assertInstanceOf(IdEnvelope::class, $message); $this->assertEquals('test-id', $message->getId()); $this->assertEquals([ - EnvelopeInterface::ENVELOPE_STACK_KEY => [ + Envelope::ENVELOPE_STACK_KEY => [ IdEnvelope::class, ], IdEnvelope::MESSAGE_ID_KEY => 'test-id', @@ -200,7 +200,7 @@ public function testSerializeEnvelopeStack(): void ], $message->getMetadata()); $this->assertEquals([ - EnvelopeInterface::ENVELOPE_STACK_KEY => [], + Envelope::ENVELOPE_STACK_KEY => [], IdEnvelope::MESSAGE_ID_KEY => 'test-id', 'message-class' => Message::class, ], $message->getMessage()->getMetadata()); From d30eea074c0bcfaffc05195c23adf35db79b7d2b Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 14 May 2026 15:35:05 +0300 Subject: [PATCH 3/4] improve --- src/Message/DelayEnvelope.php | 2 +- src/Message/Envelope.php | 6 +++--- src/Message/IdEnvelope.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Message/DelayEnvelope.php b/src/Message/DelayEnvelope.php index 52aaec84..846ecef4 100644 --- a/src/Message/DelayEnvelope.php +++ b/src/Message/DelayEnvelope.php @@ -22,6 +22,6 @@ public static function fromMessage(MessageInterface $message): static public function getDelaySeconds(): float { - return $this->getMetadata()[self::META_DELAY_SECONDS] ?? 0.0; + return $this->metadata[self::META_DELAY_SECONDS] ?? 0.0; } } diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 1e3f1544..dc122f12 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -11,12 +11,12 @@ abstract class Envelope implements MessageInterface /** @psalm-suppress MissingClassConstType */ final public const ENVELOPE_STACK_KEY = 'envelopes'; - private readonly MessageInterface $message; - /** * @psalm-var array */ - private readonly array $metadata; + protected readonly array $metadata; + + private readonly MessageInterface $message; public function __construct(MessageInterface $message, array $metadata) { diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 983eb08c..d6216d70 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -37,6 +37,6 @@ public static function fromMessage(MessageInterface $message): static public function getId(): string|int|null { - return $this->getMetadata()[self::MESSAGE_ID_KEY]; + return $this->metadata[self::MESSAGE_ID_KEY]; } } From 3e71a4bce81afccc4e449a78c8a6e1bfbf700f05 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 14 May 2026 15:38:52 +0300 Subject: [PATCH 4/4] fix --- src/Message/DelayEnvelope.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Message/DelayEnvelope.php b/src/Message/DelayEnvelope.php index 846ecef4..39525f63 100644 --- a/src/Message/DelayEnvelope.php +++ b/src/Message/DelayEnvelope.php @@ -22,6 +22,6 @@ public static function fromMessage(MessageInterface $message): static public function getDelaySeconds(): float { - return $this->metadata[self::META_DELAY_SECONDS] ?? 0.0; + return $this->metadata[self::META_DELAY_SECONDS]; } }