Skip to content

Commit 9f9201b

Browse files
SymfonyUsageProvider: Add AsMessageHandler Annotation for Symfony
1 parent f32746a commit 9f9201b

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ $ vendor/bin/phpstan
4242
- constructors, calls, factory methods
4343
- [`phpstan/phpstan-symfony`](https://github.com/phpstan/phpstan-symfony) with `containerXmlPath` must be used
4444
- `#[AsEventListener]` attribute
45+
- `#[AsMessageHandler]` attribute
4546
- `#[AsController]` attribute
4647
- `#[AsCommand]` attribute
4748
- `#[Required]` attribute

src/Provider/SymfonyUsageProvider.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ protected function shouldMarkAsUsed(ReflectionMethod $method): ?string
363363
return 'Event listener method via #[AsEventListener] attribute';
364364
}
365365

366+
if ($this->isMessageHandlerMethodWithAsMessageHandlerAttribute($method)) {
367+
return 'Message handler method via #[AsMessageHandler] attribute';
368+
}
369+
366370
if ($this->isWorkflowEventListenerMethod($method)) {
367371
return 'Workflow event listener method via workflow attribute';
368372
}
@@ -500,6 +504,32 @@ protected function isEventListenerMethodWithAsEventListenerAttribute(ReflectionM
500504
|| $this->hasAttribute($method, 'Symfony\Component\EventDispatcher\Attribute\AsEventListener');
501505
}
502506

507+
protected function isMessageHandlerMethodWithAsMessageHandlerAttribute(ReflectionMethod $method): bool
508+
{
509+
// Check if method has the attribute directly
510+
if ($this->hasAttribute($method, 'Symfony\Component\Messenger\Attribute\AsMessageHandler')) {
511+
return true;
512+
}
513+
514+
$class = $method->getDeclaringClass();
515+
516+
// Check if class has the attribute and this method is the handler method
517+
$attributes = $class->getAttributes('Symfony\Component\Messenger\Attribute\AsMessageHandler');
518+
519+
foreach ($attributes as $attribute) {
520+
$arguments = $attribute->getArguments();
521+
522+
// Get the method from attribute arguments, default to '__invoke' if not specified
523+
$handlerMethod = $arguments['method'] ?? $arguments[0] ?? '__invoke';
524+
525+
if ($handlerMethod === $method->getName()) {
526+
return true;
527+
}
528+
}
529+
530+
return false;
531+
}
532+
503533
protected function isWorkflowEventListenerMethod(ReflectionMethod $method): bool
504534
{
505535
return $this->hasAttribute($method, 'Symfony\Component\Workflow\Attribute\AsAnnounceListener')

tests/Rule/data/providers/symfony-gte71.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
66
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
7+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
78
use Symfony\Component\Routing\Attribute\Route;
89
use Symfony\Component\Workflow\Attribute\AsAnnounceListener;
910
use Symfony\Component\Workflow\Attribute\AsCompletedListener;
@@ -100,3 +101,42 @@ public function deadMethod(): void // error: Unused Symfony\WorkflowEventListene
100101
{
101102
}
102103
}
104+
105+
// Test AsMessageHandler with default __invoke method
106+
#[AsMessageHandler]
107+
class MessageHandlerWithInvoke
108+
{
109+
public function __invoke(): void
110+
{
111+
}
112+
113+
public function deadMethod(): void // error: Unused Symfony\MessageHandlerWithInvoke::deadMethod
114+
{
115+
}
116+
}
117+
118+
// Test AsMessageHandler with custom method
119+
#[AsMessageHandler(method: 'handleMessage')]
120+
class MessageHandlerWithCustomMethod
121+
{
122+
public function handleMessage(): void
123+
{
124+
}
125+
126+
public function deadMethod(): void // error: Unused Symfony\MessageHandlerWithCustomMethod::deadMethod
127+
{
128+
}
129+
}
130+
131+
// Test AsMessageHandler on method level
132+
class MessageHandlerWithMethodAttribute
133+
{
134+
#[AsMessageHandler]
135+
public function handleDirectly(): void
136+
{
137+
}
138+
139+
public function deadMethod(): void // error: Unused Symfony\MessageHandlerWithMethodAttribute::deadMethod
140+
{
141+
}
142+
}

0 commit comments

Comments
 (0)