Skip to content

Commit 0ccb0c5

Browse files
Myks92TomasVotruba
andauthored
Add support #[AsEventListener] for NoListenerWithoutContractRule (#193)
* Add support `#[AsEventListener]` for `NoListenerWithoutContractRule` * Revert fixture file --------- Co-authored-by: Tomas Votruba <[email protected]>
1 parent 1bf7b83 commit 0ccb0c5

File tree

5 files changed

+49
-2
lines changed

5 files changed

+49
-2
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1667,7 +1667,7 @@ final class SomeClass
16671667

16681668
### NoListenerWithoutContractRule
16691669

1670-
There should be no listeners modified in config. Use EventSubscriberInterface contract and PHP instead
1670+
There should be no listeners modified in config. Use EventSubscriberInterface contract or #[AsEventListener] attribute and PHP instead
16711671

16721672
```yaml
16731673
rules:
@@ -1709,6 +1709,22 @@ class SomeListener implements EventSubscriberInterface
17091709

17101710
<br>
17111711

1712+
```php
1713+
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
1714+
1715+
#[AsEventListener]
1716+
class SomeListener
1717+
{
1718+
public function __invoke()
1719+
{
1720+
}
1721+
}
1722+
```
1723+
1724+
:+1:
1725+
1726+
<br>
1727+
17121728
### NoStringInGetSubscribedEventsRule
17131729

17141730
Symfony getSubscribedEvents() method must contain only event class references, no strings

src/Enum/SymfonyClass.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ final class SymfonyClass
1919

2020
public const ABSTRACT_CONTROLLER = 'Symfony\Bundle\FrameworkBundle\Controller\AbstractController';
2121

22+
public const EVENT_LISTENER_ATTRIBUTE = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';
23+
2224
public const EVENT_SUBSCRIBER_INTERFACE = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
2325

2426
public const EVENT_DISPATCHER_INTERFACE = 'Symfony\Component\EventDispatcher\EventDispatcherInterface';

src/Rules/Symfony/NoListenerWithoutContractRule.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Node\InClassNode;
1111
use PHPStan\Rules\Rule;
1212
use PHPStan\Rules\RuleErrorBuilder;
13+
use Symplify\PHPStanRules\Enum\SymfonyClass;
1314
use Symplify\PHPStanRules\Enum\SymfonyRuleIdentifier;
1415

1516
/**
@@ -25,7 +26,7 @@ final class NoListenerWithoutContractRule implements Rule
2526
/**
2627
* @var string
2728
*/
28-
public const ERROR_MESSAGE = 'There should be no listeners modified in config. Use EventSubscriberInterface contract and PHP instead';
29+
public const ERROR_MESSAGE = 'There should be no listeners modified in config. Use EventSubscriberInterface contract or #[AsEventListener] attribute and PHP instead ';
2930

3031
/**
3132
* @see https://www.doctrine-project.org/projects/doctrine-orm/en/3.3/reference/events.html
@@ -78,6 +79,10 @@ public function processNode(Node $node, Scope $scope): array
7879
return [];
7980
}
8081

82+
if ($this->isAttributeListener($classLike)) {
83+
return [];
84+
}
85+
8186
$identifierRuleError = RuleErrorBuilder::message(self::ERROR_MESSAGE)
8287
->identifier(SymfonyRuleIdentifier::NO_LISTENER_WITHOUT_CONTRACT)
8388
->build();
@@ -96,4 +101,17 @@ private function isDoctrineListener(Class_ $class): bool
96101

97102
return false;
98103
}
104+
105+
private function isAttributeListener(Class_ $class): bool
106+
{
107+
foreach ($class->attrGroups as $attrGroup) {
108+
foreach ($attrGroup->attrs as $attr) {
109+
if ($attr->name->toString() === SymfonyClass::EVENT_LISTENER_ATTRIBUTE) {
110+
return true;
111+
}
112+
}
113+
}
114+
115+
return false;
116+
}
99117
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoListenerWithoutContractRule\Fixture;
4+
5+
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
6+
7+
#[AsEventListener]
8+
final class SomeContractedWithAttributeListener
9+
{
10+
}

tests/Rules/Symfony/NoListenerWithoutContractRule/NoListenerWithoutContractRuleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public function testRule(array $filePaths, array $expectedErrorsWithLines): void
2222
public static function provideData(): Iterator
2323
{
2424
yield [[__DIR__ . '/Fixture/SomeContractedListener.php'], []];
25+
yield [[__DIR__ . '/Fixture/SomeContractedWithAttributeListener.php'], []];
2526
yield [[__DIR__ . '/Fixture/SkipDoctrineListener.php'], []];
2627

2728
yield [[__DIR__ . '/Fixture/SomeBareListener.php'], [[

0 commit comments

Comments
 (0)