Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8700123
Convert envelope trait to abstract class
viktorprogger Dec 15, 2024
3ecc37e
Require yiisoft/arrays package
viktorprogger Dec 15, 2024
bff438d
Merge branch 'master' into convert-envelope-trait-to-abstract-class
viktorprogger Feb 14, 2025
e4e4723
Merge remote-tracking branch 'origin/master' into convert-envelope-tr…
viktorprogger Feb 14, 2025
95b6ea4
Bugfixes
viktorprogger May 4, 2025
666d650
Apply fixes from StyleCI
StyleCIBot May 4, 2025
c6b758d
Rename FailureEnvelope::$failureMeta
viktorprogger May 4, 2025
7516793
Add more typing
viktorprogger May 4, 2025
198ea47
Merge remote-tracking branch 'origin/master' into convert-envelope-tr…
viktorprogger May 8, 2025
d0deac6
Fix envelope static constructors return types
viktorprogger May 8, 2025
fefb5cb
Add tests for the new code
viktorprogger May 8, 2025
1cdcfe1
Apply fixes from StyleCI
StyleCIBot May 8, 2025
3ec891f
Re-run checks
viktorprogger May 8, 2025
b63f5e7
Fix fromMessage() return types in implementations
viktorprogger May 8, 2025
f8c47ba
Apply fixes from StyleCI
StyleCIBot May 8, 2025
4370fa0
Ignore psalm false-positives
viktorprogger May 8, 2025
f3f388b
Memorise metadata in envelopes
viktorprogger May 9, 2025
cab6ab1
Get message metadata just once
viktorprogger Jul 13, 2025
d676a3c
Address performance issues
viktorprogger Jul 13, 2025
2340afc
Merge branch 'master' into convert-envelope-trait-to-abstract-class
viktorprogger Jul 14, 2025
5d9cd41
Merge branch 'master' into convert-envelope-trait-to-abstract-class
viktorprogger Jul 14, 2025
e7acc32
Fix IdEnvelope after master merge
viktorprogger Jul 14, 2025
0c1292d
Get back 100 000 bench revisions after master merge
viktorprogger Jul 14, 2025
076e8dd
Apply fixes from StyleCI
StyleCIBot Jul 14, 2025
209ba7b
Merge branch 'refs/heads/master' into convert-envelope-trait-to-abstr…
viktorprogger Jul 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"psr/container": "^1.0 || ^2.0",
"psr/log": "^2.0 || ^3.0",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"yiisoft/arrays": "^3.1",
"yiisoft/definitions": "^3.3.1",
"yiisoft/factory": "^1.3",
"yiisoft/friendly-exception": "^1.0",
Expand Down
56 changes: 56 additions & 0 deletions src/Message/Envelope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Queue\Message;

abstract class Envelope implements EnvelopeInterface
{
private ?array $metadata = null;

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));
}

public function getMessage(): MessageInterface
{
return $this->message;
}

public function getHandlerName(): string
{
return $this->message->getHandlerName();
}

public function getData(): mixed
{
return $this->message->getData();
}

public function getMetadata(): array
{
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;
}
4 changes: 1 addition & 3 deletions src/Message/EnvelopeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +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;

public function withMessage(MessageInterface $message): self;
}
62 changes: 0 additions & 62 deletions src/Message/EnvelopeTrait.php

This file was deleted.

34 changes: 20 additions & 14 deletions src/Message/IdEnvelope.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,40 @@
/**
* 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
{
return new self($message, $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null);
}

public function setId(string|int|null $id): void
public static function fromMessage(MessageInterface $message): static
{
$this->id = $id;
/** @var mixed $rawId */
$rawId = $message->getMetadata()[self::MESSAGE_ID_KEY] ?? null;

/** @var int|string|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;
}

private function getEnvelopeMetadata(): array
protected function getEnvelopeMetadata(): array
{
return [self::MESSAGE_ID_KEY => $this->getId()];
}
Expand Down
10 changes: 1 addition & 9 deletions src/Message/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -37,12 +37,4 @@ public function getMetadata(): array
{
return $this->metadata;
}

public function withMetadata(array $metadata): self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

@viktorprogger viktorprogger May 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any real-world use cases. Do you?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Message handlers may add metadata into the message

Copy link
Contributor Author

@viktorprogger viktorprogger Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They should wrap it into an envelope then.

{
$instance = clone $this;
$instance->metadata = $metadata;

return $instance;
}
}
6 changes: 0 additions & 6 deletions src/Message/MessageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
28 changes: 15 additions & 13 deletions src/Middleware/FailureHandling/FailureEnvelope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,34 @@

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 $metadata = [],
) {
parent::__construct($message);
}

public static function fromMessage(MessageInterface $message): self
public static function fromMessage(MessageInterface $message): static
{
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);
}

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);
/** @var array $metadata */
$metadata = $this->message->getMetadata()[self::FAILURE_META_KEY] ?? [];

return $meta;
return [self::FAILURE_META_KEY => ArrayHelper::merge($metadata, $this->metadata)];
}
}
17 changes: 8 additions & 9 deletions tests/App/DummyEnvelope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@

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
public static function fromMessage(MessageInterface $message): static
{
$instance = new self();
$instance->message = $message;
return new self($message);
}

return $instance;
protected function getEnvelopeMetadata(): array
{
return [];
}
}
23 changes: 0 additions & 23 deletions tests/Unit/Message/EnvelopeTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@
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
{
private function createTestEnvelope(): DummyEnvelope
{
return new DummyEnvelope();
}

public function testFromData(): void
{
$handlerName = 'test-handler';
Expand All @@ -29,21 +23,4 @@ public function testFromData(): void
$this->assertArrayHasKey('meta', $envelope->getMetadata());
$this->assertSame('data', $envelope->getMetadata()['meta']);
}

public function testWithMessage(): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep it

Copy link
Contributor Author

@viktorprogger viktorprogger Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this better than FooEnvelope::fromMessage($message) or new FooEnvelope()?

{
$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());
}
}
Loading
Loading