-
-
Notifications
You must be signed in to change notification settings - Fork 30
Introduce MessageEncoderInterface
#308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bbb333c
7758062
2eef14d
c2f5bbb
0054f9c
579a389
5bf7f5c
3116307
d511a6e
ec5099d
892c0aa
74285e0
5e656f6
baeb4e7
89356df
eba2b5d
58a0368
218ea75
8bbfbb1
721d2ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Serializer; | ||
|
|
||
| use JsonException; | ||
|
|
||
| use const JSON_THROW_ON_ERROR; | ||
|
|
||
| /** | ||
| * Encodes and decodes queue messages using JSON format. | ||
| */ | ||
| final class JsonMessageEncoder implements MessageEncoderInterface | ||
| { | ||
| public function encode(array $data): string | ||
| { | ||
| try { | ||
| return json_encode($data, JSON_THROW_ON_ERROR); | ||
| } catch (JsonException $e) { | ||
| throw new MessageSerializerException($e->getMessage(), previous: $e); | ||
| } | ||
| } | ||
|
|
||
| public function decode(string $value): mixed | ||
| { | ||
| try { | ||
| return json_decode($value, true, 512, JSON_THROW_ON_ERROR); | ||
| } catch (JsonException $e) { | ||
| throw new MessageSerializerException($e->getMessage(), previous: $e); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Serializer; | ||
|
|
||
| /** | ||
| * Encodes and decodes a data array to and from a string. | ||
| */ | ||
| interface MessageEncoderInterface | ||
| { | ||
| /** | ||
| * Encodes a data array into a string representation. | ||
| * | ||
| * @param array $data Data to encode. Contains only scalars, nulls, and arrays — no objects or resources including array contents. | ||
| * | ||
| * @throws MessageSerializerException If encoding fails. | ||
| */ | ||
| public function encode(array $data): string; | ||
|
|
||
| /** | ||
| * Decodes a string representation back into a value. | ||
| * | ||
| * @param string $value Encoded string. | ||
| * | ||
| * @return mixed Decoded data. | ||
| * | ||
| * @throws MessageSerializerException If decoding fails. | ||
| */ | ||
| public function decode(string $value): mixed; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Serializer; | ||
|
|
||
| use Yiisoft\Queue\Message\Envelope; | ||
| use Yiisoft\Queue\Message\GenericMessage; | ||
| use Yiisoft\Queue\Message\MessageInterface; | ||
|
|
||
| use function is_array; | ||
| use function is_string; | ||
|
|
||
| /** | ||
| * Serializes and unserializes queue messages, preserving the original message class in metadata. | ||
|
samdark marked this conversation as resolved.
|
||
| * | ||
| * When serializing, assembles an array with `type`, `data`, and `meta` keys and passes it as a single array to | ||
| * {@see MessageEncoderInterface}, which encodes it to a string. When unserializing, decodes the string back to an | ||
| * array and reconstructs the original message class from the `meta` key, falling back to {@see GenericMessage} | ||
| * if the class is missing or invalid. | ||
| */ | ||
| final class MessageSerializer implements MessageSerializerInterface | ||
| { | ||
| private const META_MESSAGE_CLASS = 'message-class'; | ||
|
|
||
| public function __construct( | ||
| private readonly MessageEncoderInterface $encoder, | ||
| ) {} | ||
|
|
||
| public function serialize(MessageInterface $message): string | ||
| { | ||
| $metadata = $message->getMetadata(); | ||
|
|
||
| if (!isset($metadata[self::META_MESSAGE_CLASS])) { | ||
| $metadata[self::META_MESSAGE_CLASS] = $message instanceof Envelope | ||
| ? $message->getMessage()::class | ||
| : $message::class; | ||
| } | ||
|
|
||
| return $this->encoder->encode([ | ||
| 'type' => $message->getType(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type should be enough to get the message class from the mapping.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use one class with different types. For example: $message1 = new GenericMessage(type: 'send-email', ...);
$message2 = new GenericMessage(type: 'process-order', ...);
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is OK. Message payload will contain |
||
| 'data' => $message->getData(), | ||
| 'meta' => $metadata, | ||
| ]); | ||
| } | ||
|
|
||
| public function unserialize(string $value): MessageInterface | ||
| { | ||
| $data = $this->encoder->decode($value); | ||
|
|
||
| if (!is_array($data)) { | ||
| throw new MessageSerializerException('Decoded data must be array. Got ' . get_debug_type($data) . '.'); | ||
| } | ||
|
|
||
| $type = $data['type'] ?? null; | ||
| if (!isset($type) || !is_string($type)) { | ||
| throw new MessageSerializerException('Message type must be a string. Got ' . get_debug_type($type) . '.'); | ||
| } | ||
|
|
||
| $metadata = $data['meta'] ?? []; | ||
| if (!is_array($metadata)) { | ||
| throw new MessageSerializerException('Metadata must be an array. Got ' . get_debug_type($metadata) . '.'); | ||
| } | ||
|
|
||
| $class = $metadata[self::META_MESSAGE_CLASS] ?? GenericMessage::class; | ||
|
|
||
| // Don't check subclasses when it's a default class: that's faster | ||
| if ($class !== GenericMessage::class | ||
| && (!is_string($class) || !is_subclass_of($class, MessageInterface::class)) | ||
| ) { | ||
| $class = GenericMessage::class; | ||
| } | ||
| /** @var class-string<MessageInterface> $class */ | ||
|
|
||
| return $class::fromData($type, $data['data'] ?? null)->withMetadata($metadata); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Serializer; | ||
|
|
||
| use RuntimeException; | ||
|
|
||
| /** | ||
| * Thrown when message serialization/unserialization fails. | ||
| */ | ||
| final class MessageSerializerException extends RuntimeException {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Serializer; | ||
|
|
||
| use Yiisoft\Queue\Message\MessageInterface; | ||
|
|
||
| /** | ||
| * Serializes and unserializes queue messages to and from a string representation. | ||
| */ | ||
| interface MessageSerializerInterface | ||
| { | ||
| /** | ||
| * Serializes a message to a string. | ||
| * | ||
| * @param MessageInterface $message Message to serialize. | ||
| * | ||
| * @throws MessageSerializerException If serialization fails. | ||
| */ | ||
| public function serialize(MessageInterface $message): string; | ||
|
|
||
| /** | ||
| * Unserializes a message from a string. | ||
| * | ||
| * @param string $value Encoded message string. | ||
| * | ||
| * @throws MessageSerializerException If unserialization fails. | ||
| */ | ||
| public function unserialize(string $value): MessageInterface; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.