From 87001235894610a431bc3b8ad40f70279faddc75 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 15 Dec 2024 18:35:52 +0500 Subject: [PATCH 01/19] Convert envelope trait to abstract class --- .../{EnvelopeTrait.php => Envelope.php} | 28 +++++-------------- src/Message/EnvelopeInterface.php | 2 -- src/Message/IdEnvelope.php | 16 ++++------- src/Message/Message.php | 10 +------ src/Message/MessageInterface.php | 6 ---- .../FailureHandling/FailureEnvelope.php | 20 ++++++------- .../Message/JsonMessageSerializerTest.php | 2 +- .../SendAgainMiddlewareTest.php | 9 ++---- 8 files changed, 25 insertions(+), 68 deletions(-) rename src/Message/{EnvelopeTrait.php => Envelope.php} (60%) diff --git a/src/Message/EnvelopeTrait.php b/src/Message/Envelope.php similarity index 60% rename from src/Message/EnvelopeTrait.php rename to src/Message/Envelope.php index e8d19d65..fa51dd75 100644 --- a/src/Message/EnvelopeTrait.php +++ b/src/Message/Envelope.php @@ -4,18 +4,15 @@ namespace Yiisoft\Queue\Message; -trait EnvelopeTrait +abstract class Envelope implements EnvelopeInterface { - private MessageInterface $message; - - /** - * A mirror of {@see MessageInterface::fromData()} - */ - abstract public static function fromMessage(MessageInterface $message): self; + public function __construct(protected MessageInterface $message) + { + } public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface { - return self::fromMessage(Message::fromData($handlerName, $data, $metadata)); + return static::fromMessage(Message::fromData($handlerName, $data, $metadata)); } public function getMessage(): MessageInterface @@ -23,14 +20,6 @@ public function getMessage(): MessageInterface return $this->message; } - public function withMessage(MessageInterface $message): self - { - $instance = clone $this; - $instance->message = $message; - - return $instance; - } - public function getHandlerName(): string { return $this->message->getHandlerName(); @@ -48,15 +37,12 @@ public function getMetadata(): array [ self::ENVELOPE_STACK_KEY => array_merge( $this->message->getMetadata()[self::ENVELOPE_STACK_KEY] ?? [], - [self::class], + [static::class], ), ], $this->getEnvelopeMetadata(), ); } - public function getEnvelopeMetadata(): array - { - return []; - } + abstract protected function getEnvelopeMetadata(): array; } diff --git a/src/Message/EnvelopeInterface.php b/src/Message/EnvelopeInterface.php index 7c58daa1..72d8cced 100644 --- a/src/Message/EnvelopeInterface.php +++ b/src/Message/EnvelopeInterface.php @@ -15,6 +15,4 @@ interface EnvelopeInterface extends MessageInterface public static function fromMessage(MessageInterface $message): self; public function getMessage(): MessageInterface; - - public function withMessage(MessageInterface $message): self; } diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index bbf0d67f..4923ec47 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -7,16 +7,15 @@ /** * ID envelope allows to identify a message. */ -final class IdEnvelope implements EnvelopeInterface +final class IdEnvelope extends Envelope { - use EnvelopeTrait; - public const MESSAGE_ID_KEY = 'yii-message-id'; public function __construct( - private MessageInterface $message, - private string|int|null $id = null, + MessageInterface $message, + private readonly string|int|null $id, ) { + parent::__construct($message); } public static function fromMessage(MessageInterface $message): self @@ -24,17 +23,12 @@ public static function fromMessage(MessageInterface $message): self return new self($message, $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null); } - public function setId(string|int|null $id): void - { - $this->id = $id; - } - public function getId(): string|int|null { return $this->id ?? $this->message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; } - private function getEnvelopeMetadata(): array + protected function getEnvelopeMetadata(): array { return [self::MESSAGE_ID_KEY => $this->getId()]; } diff --git a/src/Message/Message.php b/src/Message/Message.php index ab85d069..4f153767 100644 --- a/src/Message/Message.php +++ b/src/Message/Message.php @@ -7,9 +7,9 @@ final class Message implements MessageInterface { /** + * @param string $handlerName A name of a handler which should handle this message. * @param mixed $data Message data, encodable by a queue adapter * @param array $metadata Message metadata, encodable by a queue adapter - * @param string|null $id Message id */ public function __construct( private string $handlerName, @@ -37,12 +37,4 @@ public function getMetadata(): array { return $this->metadata; } - - public function withMetadata(array $metadata): self - { - $instance = clone $this; - $instance->metadata = $metadata; - - return $instance; - } } diff --git a/src/Message/MessageInterface.php b/src/Message/MessageInterface.php index ea3b1882..a7367248 100644 --- a/src/Message/MessageInterface.php +++ b/src/Message/MessageInterface.php @@ -10,22 +10,16 @@ public static function fromData(string $handlerName, mixed $data, array $metadat /** * Returns handler name. - * - * @return string */ public function getHandlerName(): string; /** * Returns payload data. - * - * @return mixed */ public function getData(): mixed; /** * Returns message metadata: timings, attempts count, metrics, etc. - * - * @return array */ public function getMetadata(): array; } diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 0f6a62e9..f6828a5b 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -4,20 +4,19 @@ namespace Yiisoft\Queue\Middleware\FailureHandling; -use Yiisoft\Queue\Message\EnvelopeInterface; -use Yiisoft\Queue\Message\EnvelopeTrait; +use Yiisoft\Arrays\ArrayHelper; +use Yiisoft\Queue\Message\Envelope; use Yiisoft\Queue\Message\MessageInterface; -final class FailureEnvelope implements EnvelopeInterface +final class FailureEnvelope extends Envelope { - use EnvelopeTrait; - public const FAILURE_META_KEY = 'failure-meta'; public function __construct( - private MessageInterface $message, - private array $meta = [], + MessageInterface $message, + private readonly array $failureMeta = [], ) { + parent::__construct($message); } public static function fromMessage(MessageInterface $message): self @@ -25,11 +24,8 @@ public static function fromMessage(MessageInterface $message): self return new self($message, $message->getMetadata()[self::FAILURE_META_KEY] ?? []); } - public function getMetadata(): array + protected function getEnvelopeMetadata(): array { - $meta = $this->message->getMetadata(); - $meta[self::FAILURE_META_KEY] = array_merge($meta[self::FAILURE_META_KEY] ?? [], $this->meta); - - return $meta; + return [self::FAILURE_META_KEY => ArrayHelper::merge($this->message->getMetadata()[self::FAILURE_META_KEY] ?? [], $this->failureMeta)]; } } diff --git a/tests/Unit/Message/JsonMessageSerializerTest.php b/tests/Unit/Message/JsonMessageSerializerTest.php index 18bc5ed9..e14ddc14 100644 --- a/tests/Unit/Message/JsonMessageSerializerTest.php +++ b/tests/Unit/Message/JsonMessageSerializerTest.php @@ -169,7 +169,7 @@ public function testRestoreOriginalMessageClass(): void public function testRestoreOriginalMessageClassWithEnvelope(): void { - $message = new IdEnvelope(new TestMessage()); + $message = new IdEnvelope(new TestMessage(), 1); $serializer = $this->createSerializer(); $serializer->unserialize($serializer->serialize($message)); diff --git a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php index 665c6bea..92351a8d 100644 --- a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php +++ b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php @@ -151,9 +151,6 @@ public function testQueueSendingStrategies( $this->expectExceptionMessage('testException'); } - $metaInitial = [FailureEnvelope::FAILURE_META_KEY => $metaInitial]; - $metaResult = [FailureEnvelope::FAILURE_META_KEY => $metaResult]; - $handler = $this->getHandler($metaResult, $suites); $queue = $this->getPreparedQueue($metaResult, $suites); @@ -162,7 +159,7 @@ public function testQueueSendingStrategies( new Message( 'test', null, - $metaInitial + [FailureEnvelope::FAILURE_META_KEY => $metaInitial] ), new Exception('testException'), $queue @@ -194,7 +191,7 @@ private function getHandler(array $metaResult, bool $suites): MessageFailureHand $pipelineAssertion = static function (FailureHandlingRequest $request) use ( $metaResult ): FailureHandlingRequest { - Assert::assertEquals($metaResult, $request->getMessage()->getMetadata()); + Assert::assertEquals($metaResult, $request->getMessage()->getMetadata()[FailureEnvelope::FAILURE_META_KEY] ?? []); throw $request->getException(); }; @@ -209,7 +206,7 @@ private function getHandler(array $metaResult, bool $suites): MessageFailureHand private function getPreparedQueue(array $metaResult, bool $suites): QueueInterface { $queueAssertion = static function (MessageInterface $message) use ($metaResult): MessageInterface { - Assert::assertEquals($metaResult, $message->getMetadata()); + Assert::assertEquals($metaResult, $message->getMetadata()[FailureEnvelope::FAILURE_META_KEY] ?? []); return $message; }; From 3ecc37eebb3a53fb79665ea914c979824bbee700 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 15 Dec 2024 18:44:25 +0500 Subject: [PATCH 02/19] Require yiisoft/arrays package --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 3ce31e95..da66c604 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "psr/container": "^1.0|^2.0", "psr/log": "^2.0|^3.0", "symfony/console": "^5.4|^6.0", + "yiisoft/arrays": "^3.1", "yiisoft/definitions": "^1.0|^2.0|^3.0", "yiisoft/factory": "^1.3", "yiisoft/friendly-exception": "^1.0", From 95b6ea4cbda5b483b423ad9fd6e4272c9ebf188c Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 4 May 2025 18:12:49 +0500 Subject: [PATCH 03/19] Bugfixes --- src/Message/IdEnvelope.php | 16 ++++++++++++++-- tests/App/DummyEnvelope.php | 14 ++++++-------- tests/Unit/Message/EnvelopeTraitTest.php | 22 ---------------------- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 4923ec47..105967db 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -20,12 +20,24 @@ public function __construct( public static function fromMessage(MessageInterface $message): self { - return new self($message, $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null); + /** @var mixed $rawId */ + $rawId = $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; + + /** @var string|int|null $id */ + $id = match (true) { + $rawId === null => null, + is_string($rawId) => $rawId, + is_int($rawId) => $rawId, + is_object($rawId) && method_exists($rawId, '__toString') => (string)$rawId, + default => throw new \InvalidArgumentException(sprintf('Message ID must be string|int|null, %s given.', get_debug_type($rawId))), + }; + + return new self($message, $id); } public function getId(): string|int|null { - return $this->id ?? $this->message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; + return $this->id; } protected function getEnvelopeMetadata(): array diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php index 65c6162f..55f68541 100644 --- a/tests/App/DummyEnvelope.php +++ b/tests/App/DummyEnvelope.php @@ -4,19 +4,17 @@ namespace Yiisoft\Queue\Tests\App; -use Yiisoft\Queue\Message\EnvelopeInterface; -use Yiisoft\Queue\Message\EnvelopeTrait; +use Yiisoft\Queue\Message\Envelope; use Yiisoft\Queue\Message\MessageInterface; -final class DummyEnvelope implements EnvelopeInterface +final class DummyEnvelope extends Envelope { - use EnvelopeTrait; - public static function fromMessage(MessageInterface $message): self { - $instance = new self(); - $instance->message = $message; + return new self($message); + } - return $instance; + protected function getEnvelopeMetadata(): array { + return []; } } diff --git a/tests/Unit/Message/EnvelopeTraitTest.php b/tests/Unit/Message/EnvelopeTraitTest.php index 5dd5c4a6..d4d1bfb6 100644 --- a/tests/Unit/Message/EnvelopeTraitTest.php +++ b/tests/Unit/Message/EnvelopeTraitTest.php @@ -10,11 +10,6 @@ final class EnvelopeTraitTest extends TestCase { - private function createTestEnvelope(): DummyEnvelope - { - return new DummyEnvelope(); - } - public function testFromData(): void { $handlerName = 'test-handler'; @@ -29,21 +24,4 @@ public function testFromData(): void $this->assertArrayHasKey('meta', $envelope->getMetadata()); $this->assertSame('data', $envelope->getMetadata()['meta']); } - - public function testWithMessage(): void - { - $originalMessage = new Message('original-handler', 'original-data'); - $newMessage = new Message('new-handler', 'new-data'); - - $envelope = $this->createTestEnvelope(); - $envelope = $envelope->withMessage($originalMessage); - - $this->assertSame($originalMessage, $envelope->getMessage()); - - $newEnvelope = $envelope->withMessage($newMessage); - - $this->assertNotSame($envelope, $newEnvelope); - $this->assertSame($newMessage, $newEnvelope->getMessage()); - $this->assertSame($originalMessage, $envelope->getMessage()); - } } From 666d65003052275c886f257e3ad94a889598cd53 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 4 May 2025 13:13:07 +0000 Subject: [PATCH 04/19] Apply fixes from StyleCI --- src/Message/IdEnvelope.php | 2 +- src/Middleware/InvalidMiddlewareDefinitionException.php | 1 - tests/App/DummyEnvelope.php | 3 ++- tests/Unit/Message/EnvelopeTraitTest.php | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 105967db..4780ee33 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -23,7 +23,7 @@ public static function fromMessage(MessageInterface $message): self /** @var mixed $rawId */ $rawId = $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; - /** @var string|int|null $id */ + /** @var int|string|null $id */ $id = match (true) { $rawId === null => null, is_string($rawId) => $rawId, diff --git a/src/Middleware/InvalidMiddlewareDefinitionException.php b/src/Middleware/InvalidMiddlewareDefinitionException.php index 2ae2da71..9ca6ef27 100644 --- a/src/Middleware/InvalidMiddlewareDefinitionException.php +++ b/src/Middleware/InvalidMiddlewareDefinitionException.php @@ -5,7 +5,6 @@ namespace Yiisoft\Queue\Middleware; use InvalidArgumentException; - use Throwable; use function is_array; diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php index 55f68541..1be95363 100644 --- a/tests/App/DummyEnvelope.php +++ b/tests/App/DummyEnvelope.php @@ -14,7 +14,8 @@ public static function fromMessage(MessageInterface $message): self return new self($message); } - protected function getEnvelopeMetadata(): array { + protected function getEnvelopeMetadata(): array + { return []; } } diff --git a/tests/Unit/Message/EnvelopeTraitTest.php b/tests/Unit/Message/EnvelopeTraitTest.php index d4d1bfb6..6ae7f085 100644 --- a/tests/Unit/Message/EnvelopeTraitTest.php +++ b/tests/Unit/Message/EnvelopeTraitTest.php @@ -5,7 +5,6 @@ namespace Yiisoft\Queue\Tests\Unit\Message; use PHPUnit\Framework\TestCase; -use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Tests\App\DummyEnvelope; final class EnvelopeTraitTest extends TestCase From c6b758de0fc7934bad572b25b797d73a72550fef Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 4 May 2025 18:32:14 +0500 Subject: [PATCH 05/19] Rename FailureEnvelope::$failureMeta --- src/Middleware/FailureHandling/FailureEnvelope.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index f6828a5b..6b00e856 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -14,7 +14,7 @@ final class FailureEnvelope extends Envelope public function __construct( MessageInterface $message, - private readonly array $failureMeta = [], + private readonly array $metadata = [], ) { parent::__construct($message); } @@ -26,6 +26,6 @@ public static function fromMessage(MessageInterface $message): self protected function getEnvelopeMetadata(): array { - return [self::FAILURE_META_KEY => ArrayHelper::merge($this->message->getMetadata()[self::FAILURE_META_KEY] ?? [], $this->failureMeta)]; + return [self::FAILURE_META_KEY => ArrayHelper::merge($this->message->getMetadata()[self::FAILURE_META_KEY] ?? [], $this->metadata)]; } } From 7516793a952fd67e4e8582c1de26fbdafe349ab8 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 4 May 2025 18:37:14 +0500 Subject: [PATCH 06/19] Add more typing --- src/Middleware/FailureHandling/FailureEnvelope.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 6b00e856..9f1abfd3 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -21,11 +21,17 @@ public function __construct( public static function fromMessage(MessageInterface $message): self { - return new self($message, $message->getMetadata()[self::FAILURE_META_KEY] ?? []); + /** @var array $metadata */ + $metadata = $message->getMetadata()[self::FAILURE_META_KEY] ?? []; + + return new self($message, $metadata); } protected function getEnvelopeMetadata(): array { - return [self::FAILURE_META_KEY => ArrayHelper::merge($this->message->getMetadata()[self::FAILURE_META_KEY] ?? [], $this->metadata)]; + /** @var array $metadata */ + $metadata = $this->message->getMetadata()[self::FAILURE_META_KEY] ?? []; + + return [self::FAILURE_META_KEY => ArrayHelper::merge($metadata, $this->metadata)]; } } From d0deac660c20c717ec1d3aa2c823f6cb65218a7b Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Thu, 8 May 2025 16:16:27 +0500 Subject: [PATCH 07/19] Fix envelope static constructors return types --- src/Message/Envelope.php | 2 +- src/Message/EnvelopeInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 2ad6b27f..4680ed9f 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -10,7 +10,7 @@ public function __construct(protected MessageInterface $message) { } - public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface + public static function fromData(string $handlerName, mixed $data, array $metadata = []): static { return static::fromMessage(Message::fromData($handlerName, $data, $metadata)); } diff --git a/src/Message/EnvelopeInterface.php b/src/Message/EnvelopeInterface.php index 72d8cced..612fe368 100644 --- a/src/Message/EnvelopeInterface.php +++ b/src/Message/EnvelopeInterface.php @@ -12,7 +12,7 @@ interface EnvelopeInterface extends MessageInterface /** @psalm-suppress MissingClassConstType */ public const ENVELOPE_STACK_KEY = 'envelopes'; - public static function fromMessage(MessageInterface $message): self; + public static function fromMessage(MessageInterface $message): static; public function getMessage(): MessageInterface; } From fefb5cb6eaec6a6a93a35823d2ac9d6667acea13 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Thu, 8 May 2025 16:16:45 +0500 Subject: [PATCH 08/19] Add tests for the new code --- tests/Unit/Message/IdEnvelopeTest.php | 114 ++++++++++++++++++ .../FailureHandling/FailureEnvelopeTest.php | 83 +++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 tests/Unit/Message/IdEnvelopeTest.php create mode 100644 tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php diff --git a/tests/Unit/Message/IdEnvelopeTest.php b/tests/Unit/Message/IdEnvelopeTest.php new file mode 100644 index 00000000..0235e88e --- /dev/null +++ b/tests/Unit/Message/IdEnvelopeTest.php @@ -0,0 +1,114 @@ +createMessage(); + $id = 'test-id'; + + $envelope = new IdEnvelope($message, $id); + + $this->assertSame($message, $envelope->getMessage()); + $this->assertSame($id, $envelope->getId()); + } + + public function testFromMessageWithStringId(): void + { + $id = 'test-id'; + $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $id]); + + $envelope = IdEnvelope::fromMessage($message); + + $this->assertSame($id, $envelope->getId()); + } + + public function testFromMessageWithIntId(): void + { + $id = 123; + $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $id]); + + $envelope = IdEnvelope::fromMessage($message); + + $this->assertSame($id, $envelope->getId()); + } + + public function testFromMessageWithNullId(): void + { + $message = $this->createMessage(); + + $envelope = IdEnvelope::fromMessage($message); + + $this->assertNull($envelope->getId()); + } + + public function testFromMessageWithObjectHavingToString(): void + { + $stringableObject = new class() { + public function __toString(): string + { + return 'object-id'; + } + }; + + $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $stringableObject]); + + $envelope = IdEnvelope::fromMessage($message); + + $this->assertSame('object-id', $envelope->getId()); + } + + public function testFromMessageWithInvalidIdType(): void + { + $invalidId = ['array-cannot-be-id']; + $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $invalidId]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Message ID must be string|int|null, array given.'); + + IdEnvelope::fromMessage($message); + } + + public function testGetEnvelopeMetadata(): void + { + $id = 'test-id'; + $message = $this->createMessage(); + $envelope = new IdEnvelope($message, $id); + + $metadata = $envelope->getMetadata(); + + $this->assertArrayHasKey(IdEnvelope::MESSAGE_ID_KEY, $metadata); + $this->assertSame($id, $metadata[IdEnvelope::MESSAGE_ID_KEY]); + } + + public function testFromData(): void + { + $handlerName = 'test-handler'; + $data = ['key' => 'value']; + $metadata = ['meta' => 'data', IdEnvelope::MESSAGE_ID_KEY => 'test-id']; + + $envelope = IdEnvelope::fromData($handlerName, $data, $metadata); + + $this->assertInstanceOf(IdEnvelope::class, $envelope); + $this->assertSame($handlerName, $envelope->getHandlerName()); + $this->assertSame($data, $envelope->getData()); + $this->assertArrayHasKey('meta', $envelope->getMetadata()); + $this->assertSame('data', $envelope->getMetadata()['meta']); + $this->assertSame('test-id', $envelope->getId()); + } + + private function createMessage(array $metadata = []): MessageInterface + { + return new Message('test-handler', ['test-data'], $metadata); + } +} diff --git a/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php b/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php new file mode 100644 index 00000000..6500c971 --- /dev/null +++ b/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php @@ -0,0 +1,83 @@ +createMessage(); + $metadata = ['attempt' => 1, 'error' => 'Test error']; + + $envelope = new FailureEnvelope($message, $metadata); + + $this->assertSame($message, $envelope->getMessage()); + $this->assertArrayHasKey(FailureEnvelope::FAILURE_META_KEY, $envelope->getMetadata()); + $this->assertSame($metadata, $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); + } + + public function testFromMessageWithExistingMetadata(): void + { + $existingMetadata = ['attempt' => 1]; + $message = $this->createMessage([FailureEnvelope::FAILURE_META_KEY => $existingMetadata]); + + $envelope = FailureEnvelope::fromMessage($message); + + $this->assertSame($existingMetadata, $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); + } + + public function testFromMessageWithoutMetadata(): void + { + $message = $this->createMessage(); + + $envelope = FailureEnvelope::fromMessage($message); + + $this->assertArrayHasKey(FailureEnvelope::FAILURE_META_KEY, $envelope->getMetadata()); + $this->assertSame([], $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); + } + + public function testMetadataMerging(): void + { + $existingMetadata = ['attempt' => 1, 'firstError' => 'First error']; + $message = $this->createMessage([FailureEnvelope::FAILURE_META_KEY => $existingMetadata]); + $newMetadata = ['attempt' => 2, 'lastError' => 'Last error']; + + $envelope = new FailureEnvelope($message, $newMetadata); + + $mergedMetadata = $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]; + $this->assertSame(2, $mergedMetadata['attempt']); + $this->assertSame('First error', $mergedMetadata['firstError']); + $this->assertSame('Last error', $mergedMetadata['lastError']); + } + + public function testFromData(): void + { + $handlerName = 'test-handler'; + $data = ['key' => 'value']; + $metadata = [ + 'meta' => 'data', + FailureEnvelope::FAILURE_META_KEY => ['attempt' => 1], + ]; + + $envelope = FailureEnvelope::fromData($handlerName, $data, $metadata); + + $this->assertInstanceOf(FailureEnvelope::class, $envelope); + $this->assertSame($handlerName, $envelope->getHandlerName()); + $this->assertSame($data, $envelope->getData()); + $this->assertArrayHasKey('meta', $envelope->getMetadata()); + $this->assertSame('data', $envelope->getMetadata()['meta']); + $this->assertSame(['attempt' => 1], $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); + } + + private function createMessage(array $metadata = []): MessageInterface + { + return new Message('test-handler', ['test-data'], $metadata); + } +} From 1cdcfe108778cd4ab2713362d99a3009219881da Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 8 May 2025 11:16:59 +0000 Subject: [PATCH 09/19] Apply fixes from StyleCI --- tests/Unit/Message/IdEnvelopeTest.php | 2 +- .../FailureHandling/FailureEnvelopeTest.php | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/Unit/Message/IdEnvelopeTest.php b/tests/Unit/Message/IdEnvelopeTest.php index 0235e88e..fa6762a4 100644 --- a/tests/Unit/Message/IdEnvelopeTest.php +++ b/tests/Unit/Message/IdEnvelopeTest.php @@ -54,7 +54,7 @@ public function testFromMessageWithNullId(): void public function testFromMessageWithObjectHavingToString(): void { - $stringableObject = new class() { + $stringableObject = new class () { public function __toString(): string { return 'object-id'; diff --git a/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php b/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php index 6500c971..e3991cc7 100644 --- a/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php +++ b/tests/Unit/Middleware/FailureHandling/FailureEnvelopeTest.php @@ -15,48 +15,48 @@ public function testConstructor(): void { $message = $this->createMessage(); $metadata = ['attempt' => 1, 'error' => 'Test error']; - + $envelope = new FailureEnvelope($message, $metadata); - + $this->assertSame($message, $envelope->getMessage()); $this->assertArrayHasKey(FailureEnvelope::FAILURE_META_KEY, $envelope->getMetadata()); $this->assertSame($metadata, $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); } - + public function testFromMessageWithExistingMetadata(): void { $existingMetadata = ['attempt' => 1]; $message = $this->createMessage([FailureEnvelope::FAILURE_META_KEY => $existingMetadata]); - + $envelope = FailureEnvelope::fromMessage($message); - + $this->assertSame($existingMetadata, $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); } - + public function testFromMessageWithoutMetadata(): void { $message = $this->createMessage(); - + $envelope = FailureEnvelope::fromMessage($message); - + $this->assertArrayHasKey(FailureEnvelope::FAILURE_META_KEY, $envelope->getMetadata()); $this->assertSame([], $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); } - + public function testMetadataMerging(): void { $existingMetadata = ['attempt' => 1, 'firstError' => 'First error']; $message = $this->createMessage([FailureEnvelope::FAILURE_META_KEY => $existingMetadata]); $newMetadata = ['attempt' => 2, 'lastError' => 'Last error']; - + $envelope = new FailureEnvelope($message, $newMetadata); - + $mergedMetadata = $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]; $this->assertSame(2, $mergedMetadata['attempt']); $this->assertSame('First error', $mergedMetadata['firstError']); $this->assertSame('Last error', $mergedMetadata['lastError']); } - + public function testFromData(): void { $handlerName = 'test-handler'; @@ -65,9 +65,9 @@ public function testFromData(): void 'meta' => 'data', FailureEnvelope::FAILURE_META_KEY => ['attempt' => 1], ]; - + $envelope = FailureEnvelope::fromData($handlerName, $data, $metadata); - + $this->assertInstanceOf(FailureEnvelope::class, $envelope); $this->assertSame($handlerName, $envelope->getHandlerName()); $this->assertSame($data, $envelope->getData()); @@ -75,7 +75,7 @@ public function testFromData(): void $this->assertSame('data', $envelope->getMetadata()['meta']); $this->assertSame(['attempt' => 1], $envelope->getMetadata()[FailureEnvelope::FAILURE_META_KEY]); } - + private function createMessage(array $metadata = []): MessageInterface { return new Message('test-handler', ['test-data'], $metadata); From 3ec891f1f7b01101fb9adabee73ade9fdadbfded Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Thu, 8 May 2025 16:28:33 +0500 Subject: [PATCH 10/19] Re-run checks --- tests/Unit/Message/IdEnvelopeTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Unit/Message/IdEnvelopeTest.php b/tests/Unit/Message/IdEnvelopeTest.php index fa6762a4..a02f9e5b 100644 --- a/tests/Unit/Message/IdEnvelopeTest.php +++ b/tests/Unit/Message/IdEnvelopeTest.php @@ -60,9 +60,7 @@ public function __toString(): string return 'object-id'; } }; - $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $stringableObject]); - $envelope = IdEnvelope::fromMessage($message); $this->assertSame('object-id', $envelope->getId()); From b63f5e7c33850eb5f2cd233926a48f7da4e99c0c Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Thu, 8 May 2025 16:31:14 +0500 Subject: [PATCH 11/19] Fix fromMessage() return types in implementations --- src/Message/IdEnvelope.php | 2 +- src/Middleware/FailureHandling/FailureEnvelope.php | 4 ++-- tests/App/DummyEnvelope.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 4780ee33..3907e7ee 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -18,7 +18,7 @@ public function __construct( parent::__construct($message); } - public static function fromMessage(MessageInterface $message): self + public static function fromMessage(MessageInterface $message): static { /** @var mixed $rawId */ $rawId = $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 9f1abfd3..8f347870 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -19,12 +19,12 @@ public function __construct( parent::__construct($message); } - public static function fromMessage(MessageInterface $message): self + public static function fromMessage(MessageInterface $message): static { /** @var array $metadata */ $metadata = $message->getMetadata()[self::FAILURE_META_KEY] ?? []; - return new self($message, $metadata); + return new static($message, $metadata); } protected function getEnvelopeMetadata(): array diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php index 1be95363..c4eef55c 100644 --- a/tests/App/DummyEnvelope.php +++ b/tests/App/DummyEnvelope.php @@ -9,9 +9,9 @@ final class DummyEnvelope extends Envelope { - public static function fromMessage(MessageInterface $message): self + public static function fromMessage(MessageInterface $message): static { - return new self($message); + return new static($message); } protected function getEnvelopeMetadata(): array From f8c47ba79ed7a2158ded34e84b012b2e5f1e2085 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 8 May 2025 11:31:23 +0000 Subject: [PATCH 12/19] Apply fixes from StyleCI --- src/Middleware/FailureHandling/FailureEnvelope.php | 2 +- tests/App/DummyEnvelope.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 8f347870..9b9f8515 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -24,7 +24,7 @@ public static function fromMessage(MessageInterface $message): static /** @var array $metadata */ $metadata = $message->getMetadata()[self::FAILURE_META_KEY] ?? []; - return new static($message, $metadata); + return new self($message, $metadata); } protected function getEnvelopeMetadata(): array diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php index c4eef55c..dc1d1f7d 100644 --- a/tests/App/DummyEnvelope.php +++ b/tests/App/DummyEnvelope.php @@ -11,7 +11,7 @@ final class DummyEnvelope extends Envelope { public static function fromMessage(MessageInterface $message): static { - return new static($message); + return new self($message); } protected function getEnvelopeMetadata(): array From 4370fa0b93bc7031fe53c5595f41d2d1662bdbe6 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Thu, 8 May 2025 16:39:04 +0500 Subject: [PATCH 13/19] Ignore psalm false-positives --- src/Message/Envelope.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 4680ed9f..2440b29b 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -10,8 +10,10 @@ public function __construct(protected MessageInterface $message) { } + /** @psalm-suppress MoreSpecificReturnType */ public static function fromData(string $handlerName, mixed $data, array $metadata = []): static { + /** @psalm-suppress LessSpecificReturnStatement */ return static::fromMessage(Message::fromData($handlerName, $data, $metadata)); } From f3f388b0860736f6f37f31f3d7d7ef94205a951b Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Fri, 9 May 2025 20:43:32 +0500 Subject: [PATCH 14/19] Memorise metadata in envelopes --- src/Message/Envelope.php | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index 2440b29b..d171e3e7 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -6,6 +6,8 @@ abstract class Envelope implements EnvelopeInterface { + private ?array $metadata = null; + public function __construct(protected MessageInterface $message) { } @@ -34,16 +36,20 @@ public function getData(): mixed public function getMetadata(): array { - return array_merge( - $this->message->getMetadata(), - [ - EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( - $this->message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? [], - [static::class], - ), - ], - $this->getEnvelopeMetadata(), - ); + if ($this->metadata === null) { + $this->metadata = array_merge( + $this->message->getMetadata(), + [ + EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( + $this->message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? [], + [static::class], + ), + ], + $this->getEnvelopeMetadata(), + ); + } + + return $this->metadata; } abstract protected function getEnvelopeMetadata(): array; From cab6ab1cc92a96057f6512fcc84b751ddd716d4c Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 13 Jul 2025 18:23:24 +0500 Subject: [PATCH 15/19] Get message metadata just once --- src/Message/Envelope.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Message/Envelope.php b/src/Message/Envelope.php index d171e3e7..a39668d6 100644 --- a/src/Message/Envelope.php +++ b/src/Message/Envelope.php @@ -37,11 +37,18 @@ 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 = []; + } + $this->metadata = array_merge( - $this->message->getMetadata(), + $messageMeta, [ EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( - $this->message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? [], + $stack, [static::class], ), ], From d676a3c11896e8aa6beef8dfac11b31df550d7e8 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 13 Jul 2025 18:36:06 +0500 Subject: [PATCH 16/19] Address performance issues --- src/Message/IdEnvelope.php | 3 +-- src/Middleware/FailureHandling/FailureEnvelope.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index 3907e7ee..ed1dcdd7 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -12,10 +12,9 @@ final class IdEnvelope extends Envelope public const MESSAGE_ID_KEY = 'yii-message-id'; public function __construct( - MessageInterface $message, + protected MessageInterface $message, private readonly string|int|null $id, ) { - parent::__construct($message); } public static function fromMessage(MessageInterface $message): static diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Middleware/FailureHandling/FailureEnvelope.php index 9b9f8515..4d795588 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Middleware/FailureHandling/FailureEnvelope.php @@ -13,10 +13,9 @@ final class FailureEnvelope extends Envelope public const FAILURE_META_KEY = 'failure-meta'; public function __construct( - MessageInterface $message, + protected MessageInterface $message, private readonly array $metadata = [], ) { - parent::__construct($message); } public static function fromMessage(MessageInterface $message): static From e7acc3206fc25defb4e63c998278a2b3385ff3df Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Mon, 14 Jul 2025 19:55:28 +0500 Subject: [PATCH 17/19] Fix IdEnvelope after master merge --- src/Message/IdEnvelope.php | 6 ++++-- tests/Unit/Message/IdEnvelopeTest.php | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index ed1dcdd7..c93fb492 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -4,6 +4,8 @@ namespace Yiisoft\Queue\Message; +use Stringable; + /** * ID envelope allows to identify a message. */ @@ -24,11 +26,11 @@ public static function fromMessage(MessageInterface $message): static /** @var int|string|null $id */ $id = match (true) { - $rawId === null => null, + $rawId === null => null, // don't remove this branch: it's important for compute speed is_string($rawId) => $rawId, is_int($rawId) => $rawId, is_object($rawId) && method_exists($rawId, '__toString') => (string)$rawId, - default => throw new \InvalidArgumentException(sprintf('Message ID must be string|int|null, %s given.', get_debug_type($rawId))), + default => null, }; return new self($message, $id); diff --git a/tests/Unit/Message/IdEnvelopeTest.php b/tests/Unit/Message/IdEnvelopeTest.php index fd173eae..fbd5acc8 100644 --- a/tests/Unit/Message/IdEnvelopeTest.php +++ b/tests/Unit/Message/IdEnvelopeTest.php @@ -70,6 +70,7 @@ public function testFromMessageWithInvalidIdType(): void { $invalidId = ['array-cannot-be-id']; $message = $this->createMessage([IdEnvelope::MESSAGE_ID_KEY => $invalidId]); + $message = IdEnvelope::fromMessage($message); $this->assertNull($message->getId()); } From 0c1292d78b761de806aa6fcf6452016b5aa795c9 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Mon, 14 Jul 2025 19:56:29 +0500 Subject: [PATCH 18/19] Get back 100 000 bench revisions after master merge --- phpbench.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpbench.json b/phpbench.json index 0fa8d4e8..04436028 100644 --- a/phpbench.json +++ b/phpbench.json @@ -2,7 +2,7 @@ "$schema":"./vendor/phpbench/phpbench/phpbench.schema.json", "runner.bootstrap": "vendor/autoload.php", "runner.path": "tests/Benchmark", - "runner.revs": 10000, + "runner.revs": 100000, "runner.iterations": 5, "runner.warmup": 5 } From 076e8dd661934b815471ad296d935df05c8a55de Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 14 Jul 2025 14:56:56 +0000 Subject: [PATCH 19/19] Apply fixes from StyleCI --- src/Message/IdEnvelope.php | 2 -- tests/Unit/Message/IdEnvelopeTest.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index c93fb492..8033a988 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -4,8 +4,6 @@ namespace Yiisoft\Queue\Message; -use Stringable; - /** * ID envelope allows to identify a message. */ diff --git a/tests/Unit/Message/IdEnvelopeTest.php b/tests/Unit/Message/IdEnvelopeTest.php index fbd5acc8..b66e958d 100644 --- a/tests/Unit/Message/IdEnvelopeTest.php +++ b/tests/Unit/Message/IdEnvelopeTest.php @@ -4,7 +4,6 @@ namespace Yiisoft\Queue\Tests\Unit\Message; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\Message;