From 747bb44bbd3aa64fd3830d7a68663d46a0d03168 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Sun, 24 Nov 2024 20:48:10 -0500 Subject: [PATCH] feat(history): allow `#[TagStamp]` to be added to parent classes/interfaces --- README.md | 4 +- .../ReceiveMonitorStampListener.php | 4 +- src/History/Model/Tags.php | 8 +-- src/Stamp/AttributeStamp.php | 62 +++++++++++++++++++ src/Stamp/DisableMonitoringStamp.php | 30 +-------- src/Stamp/TagStamp.php | 3 +- tests/Unit/History/Model/TagsTest.php | 14 ++++- 7 files changed, 82 insertions(+), 43 deletions(-) create mode 100644 src/Stamp/AttributeStamp.php diff --git a/README.md b/README.md index 2c9418b..83217a6 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ add your own in one of two ways: $bus->dispatch(new MyMessage(), [new TagStamp('tag-1'), new TagStamp('tag-2')]) ``` -2. Add the `TagStamp` as a class attribute to your message: +2. Add the `TagStamp` as a class attribute to your message (and parent class/interface): ```php use Zenstruck\Messenger\Monitor\Stamp\TagStamp; @@ -164,6 +164,8 @@ add your own in one of two ways: { } ``` + > [!TIP] + > You can also add the `TagStamp` attribute to parent classes/interfaces. #### `messenger:monitor:purge` Command diff --git a/src/EventListener/ReceiveMonitorStampListener.php b/src/EventListener/ReceiveMonitorStampListener.php index ad9e14c..36ced8e 100644 --- a/src/EventListener/ReceiveMonitorStampListener.php +++ b/src/EventListener/ReceiveMonitorStampListener.php @@ -65,9 +65,7 @@ private function isMonitoringDisabled(Envelope $envelope): bool } } - $stamp = $envelope->last(DisableMonitoringStamp::class) ?? DisableMonitoringStamp::getFor($messageClass); - - if (!$stamp) { + if (!$stamp = DisableMonitoringStamp::firstFrom($envelope)) { return false; } diff --git a/src/History/Model/Tags.php b/src/History/Model/Tags.php index cd5cbee..34dfa79 100644 --- a/src/History/Model/Tags.php +++ b/src/History/Model/Tags.php @@ -100,12 +100,8 @@ static function(string $tag): array { */ private static function parseFrom(Envelope $envelope): \Traversable { - foreach ((new \ReflectionClass($envelope->getMessage()))->getAttributes(TagStamp::class) as $attribute) { - yield $attribute->newInstance()->value; - } - - foreach ($envelope->all(TagStamp::class) as $tag) { - yield $tag->value; // @phpstan-ignore-line + foreach (TagStamp::from($envelope) as $tag) { + yield $tag->value; } } } diff --git a/src/Stamp/AttributeStamp.php b/src/Stamp/AttributeStamp.php new file mode 100644 index 0000000..c21b316 --- /dev/null +++ b/src/Stamp/AttributeStamp.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Messenger\Monitor\Stamp; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\StampInterface; + +/** + * @author Kevin Bond + */ +abstract class AttributeStamp implements StampInterface +{ + /** + * @internal + * + * @return static[] + */ + final public static function from(Envelope $envelope): iterable + { + foreach ($envelope->all(static::class) as $stamp) { + yield $stamp; // @phpstan-ignore generator.valueType + } + + $original = $reflection = new \ReflectionClass($envelope->getMessage()); + + while (false !== $reflection) { + + foreach ($reflection->getAttributes(static::class) as $attribute) { + yield $attribute->newInstance(); + } + + $reflection = $reflection->getParentClass(); + } + + foreach ($original->getInterfaces() as $refInterface) { + foreach ($refInterface->getAttributes(static::class) as $attribute) { + yield $attribute->newInstance(); + } + } + } + + /** + * @internal + */ + final public static function firstFrom(Envelope $envelope): ?static + { + foreach (self::from($envelope) as $stamp) { + return $stamp; + } + + return null; + } +} diff --git a/src/Stamp/DisableMonitoringStamp.php b/src/Stamp/DisableMonitoringStamp.php index 548cd59..c7d6645 100644 --- a/src/Stamp/DisableMonitoringStamp.php +++ b/src/Stamp/DisableMonitoringStamp.php @@ -11,41 +11,13 @@ namespace Zenstruck\Messenger\Monitor\Stamp; -use Symfony\Component\Messenger\Stamp\StampInterface; - /** * @author Kevin Bond */ #[\Attribute(\Attribute::TARGET_CLASS)] -final class DisableMonitoringStamp implements StampInterface +final class DisableMonitoringStamp extends AttributeStamp { public function __construct(public readonly bool $onlyWhenNoHandler = false) { } - - /** - * @internal - * - * @param class-string $class - */ - public static function getFor(string $class): ?self - { - $original = $reflection = new \ReflectionClass($class); - - while (false !== $reflection) { - if ($attributes = $reflection->getAttributes(self::class)) { - return $attributes[0]->newInstance(); - } - - $reflection = $reflection->getParentClass(); - } - - foreach ($original->getInterfaces() as $refInterface) { - if ($attributes = $refInterface->getAttributes(self::class)) { - return $attributes[0]->newInstance(); - } - } - - return null; - } } diff --git a/src/Stamp/TagStamp.php b/src/Stamp/TagStamp.php index df5b7b6..522bcb6 100644 --- a/src/Stamp/TagStamp.php +++ b/src/Stamp/TagStamp.php @@ -11,7 +11,6 @@ namespace Zenstruck\Messenger\Monitor\Stamp; -use Symfony\Component\Messenger\Stamp\StampInterface; use Symfony\Component\Scheduler\Messenger\ScheduledStamp; use Zenstruck\Messenger\Monitor\Schedule\TaskInfo; @@ -19,7 +18,7 @@ * @author Kevin Bond */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] -final class TagStamp implements StampInterface, \Stringable +final class TagStamp extends AttributeStamp implements \Stringable { public function __construct( public readonly string $value, diff --git a/tests/Unit/History/Model/TagsTest.php b/tests/Unit/History/Model/TagsTest.php index 28c2779..fe998fe 100644 --- a/tests/Unit/History/Model/TagsTest.php +++ b/tests/Unit/History/Model/TagsTest.php @@ -87,7 +87,7 @@ public function create_from_envelope(): void new TagStamp('qux'), ]); - $this->assertSame(['from', 'attribute', 'bar', 'foo', 'baz', 'qux'], (new Tags($envelope))->all()); + $this->assertSame(['foo', 'bar', 'baz', 'qux', 'from', 'attribute', 'abstract', 'interface'], (new Tags($envelope))->all()); } /** @@ -104,9 +104,19 @@ public function expand(): void } } +#[TagStamp('interface')] +interface InterfaceMessage +{ +} + +#[TagStamp('abstract')] +abstract class AbstractMessage implements InterfaceMessage +{ +} + #[TagStamp('from')] #[TagStamp('attribute')] #[TagStamp('bar')] -class TestMessage +class TestMessage extends AbstractMessage { }