Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 61b86c6

Browse files
authored
Add RemoveEventSubscriberDescriptionFixer (#40)
* Add RemoveEventSubscriberDescriptionFixer to drop event handler docblocks Removes docblock descriptions on event subscriber methods where the words only repeat the method name plus "event", e.g. a "Delete lead event" description on onLeadDelete(LeadEvent $event). * Only fix public methods; keep other docblock tags like @param Restrict removal to public methods and strip just the matching description line, preserving @param/@return tags instead of skipping the whole docblock.
1 parent 618cae1 commit 61b86c6

8 files changed

Lines changed: 312 additions & 0 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\CodingStandard\Fixer\Annotation;
6+
7+
use PhpCsFixer\FixerDefinition\FixerDefinition;
8+
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
9+
use PhpCsFixer\Tokenizer\Token;
10+
use PhpCsFixer\Tokenizer\Tokens;
11+
use SplFileInfo;
12+
use Symplify\CodingStandard\Fixer\AbstractSymplifyFixer;
13+
use Symplify\CodingStandard\Fixer\Naming\MethodNameResolver;
14+
use Symplify\CodingStandard\TokenRunner\Traverser\TokenReverser;
15+
16+
/**
17+
* @see \Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\RemoveEventSubscriberDescriptionFixerTest
18+
*/
19+
final class RemoveEventSubscriberDescriptionFixer extends AbstractSymplifyFixer
20+
{
21+
private const string ERROR_MESSAGE = 'Remove event subscriber docblock description that only repeats the method name words plus "event"';
22+
23+
private readonly MethodNameResolver $methodNameResolver;
24+
25+
public function __construct(
26+
private readonly TokenReverser $tokenReverser
27+
) {
28+
$this->methodNameResolver = new MethodNameResolver();
29+
}
30+
31+
public function getDefinition(): FixerDefinitionInterface
32+
{
33+
return new FixerDefinition(self::ERROR_MESSAGE, []);
34+
}
35+
36+
/**
37+
* @param Tokens<Token> $tokens
38+
*/
39+
public function isCandidate(Tokens $tokens): bool
40+
{
41+
if (! $tokens->isTokenKindFound(T_FUNCTION)) {
42+
return false;
43+
}
44+
45+
return $tokens->isAnyTokenKindsFound([T_DOC_COMMENT, T_COMMENT]);
46+
}
47+
48+
/**
49+
* @param Tokens<Token> $tokens
50+
*/
51+
public function fix(SplFileInfo $fileInfo, Tokens $tokens): void
52+
{
53+
$reversedTokens = $this->tokenReverser->reverse($tokens);
54+
55+
foreach ($reversedTokens as $index => $token) {
56+
if (! $token->isGivenKind([T_DOC_COMMENT, T_COMMENT])) {
57+
continue;
58+
}
59+
60+
$functionIndex = $this->resolveFunctionIndex($tokens, $index);
61+
if ($functionIndex === null) {
62+
continue;
63+
}
64+
65+
// only handle public methods (event subscriber handlers)
66+
if (! $this->isPublicMethod($tokens, $index, $functionIndex)) {
67+
continue;
68+
}
69+
70+
$methodName = $this->methodNameResolver->resolve($tokens, $index);
71+
if ($methodName === null) {
72+
continue;
73+
}
74+
75+
$docblockLines = explode("\n", $token->getContent());
76+
77+
$hasChanged = false;
78+
foreach ($docblockLines as $key => $docblockLine) {
79+
if (! $this->isEventDescriptionLine($docblockLine, $methodName)) {
80+
continue;
81+
}
82+
83+
unset($docblockLines[$key]);
84+
$hasChanged = true;
85+
}
86+
87+
if (! $hasChanged) {
88+
continue;
89+
}
90+
91+
if ($this->isEmptyDocblock($docblockLines)) {
92+
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
93+
} else {
94+
$tokens[$index] = new Token([T_DOC_COMMENT, implode("\n", $docblockLines)]);
95+
}
96+
}
97+
}
98+
99+
/**
100+
* @param Tokens<Token> $tokens
101+
*/
102+
private function resolveFunctionIndex(Tokens $tokens, int $commentIndex): ?int
103+
{
104+
foreach ($tokens as $position => $token) {
105+
if ($position <= $commentIndex) {
106+
continue;
107+
}
108+
109+
if ($token->isGivenKind(T_FUNCTION)) {
110+
return $position;
111+
}
112+
}
113+
114+
return null;
115+
}
116+
117+
/**
118+
* @param Tokens<Token> $tokens
119+
*/
120+
private function isPublicMethod(Tokens $tokens, int $commentIndex, int $functionIndex): bool
121+
{
122+
for ($position = $commentIndex + 1; $position < $functionIndex; ++$position) {
123+
if ($tokens[$position]->isGivenKind([T_PRIVATE, T_PROTECTED])) {
124+
return false;
125+
}
126+
}
127+
128+
return true;
129+
}
130+
131+
private function isEventDescriptionLine(string $docblockLine, string $methodName): bool
132+
{
133+
$descriptionWords = $this->resolveWords($docblockLine);
134+
135+
// must be an "... event" description, otherwise leave it to duplicate-description fixer
136+
if (! in_array('event', $descriptionWords, true)) {
137+
return false;
138+
}
139+
140+
$descriptionWords = array_filter(
141+
$descriptionWords,
142+
static fn (string $word): bool => $word !== 'event'
143+
);
144+
145+
if ($descriptionWords === []) {
146+
return false;
147+
}
148+
149+
$methodWords = array_filter(
150+
$this->resolveWords($methodName),
151+
// event subscriber handlers are commonly prefixed with "on"
152+
static fn (string $word): bool => $word !== 'on'
153+
);
154+
155+
sort($descriptionWords);
156+
sort($methodWords);
157+
158+
return $descriptionWords === $methodWords;
159+
}
160+
161+
/**
162+
* @param string[] $docblockLines
163+
*/
164+
private function isEmptyDocblock(array $docblockLines): bool
165+
{
166+
$bareContent = preg_replace('#[/*\s]+#', '', implode('', $docblockLines));
167+
168+
return $bareContent === '';
169+
}
170+
171+
/**
172+
* @return string[]
173+
*/
174+
private function resolveWords(string $value): array
175+
{
176+
// split camelCase boundaries, e.g. "onLeadDelete" => "on Lead Delete"
177+
$spaced = (string) preg_replace('#(?<=[a-z])(?=[A-Z])#', ' ', $value);
178+
179+
preg_match_all('#[a-zA-Z]+#', strtolower($spaced), $matches);
180+
181+
return $matches[0];
182+
}
183+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
4+
5+
final class OnLeadDelete
6+
{
7+
/**
8+
* Delete lead event
9+
*/
10+
public function onLeadDelete(LeadEvent $event): bool
11+
{
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
20+
21+
final class OnLeadDelete
22+
{
23+
24+
public function onLeadDelete(LeadEvent $event): bool
25+
{
26+
}
27+
}
28+
29+
?>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
4+
5+
final class ParamTagKept
6+
{
7+
/**
8+
* Delete lead event
9+
*
10+
* @param LeadEvent $event
11+
*/
12+
public function onLeadDelete(LeadEvent $event): bool
13+
{
14+
}
15+
}
16+
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
22+
23+
final class ParamTagKept
24+
{
25+
/**
26+
*
27+
* @param LeadEvent $event
28+
*/
29+
public function onLeadDelete(LeadEvent $event): bool
30+
{
31+
}
32+
}
33+
34+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
4+
5+
final class SkipNonMatchingWords
6+
{
7+
/**
8+
* Send welcome event
9+
*/
10+
public function onLeadDelete(LeadEvent $event): bool
11+
{
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer\Fixture;
4+
5+
final class SkipPrivateMethod
6+
{
7+
/**
8+
* Delete lead event
9+
*/
10+
private function onLeadDelete(LeadEvent $event): bool
11+
{
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Symplify\EasyCodingStandard\Testing\PHPUnit\AbstractCheckerTestCase;
10+
11+
final class RemoveEventSubscriberDescriptionFixerTest extends AbstractCheckerTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFiles(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfig(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Symplify\CodingStandard\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer;
6+
use Symplify\EasyCodingStandard\Config\ECSConfig;
7+
8+
return static function (ECSConfig $ecsConfig): void {
9+
$ecsConfig->rules([RemoveEventSubscriberDescriptionFixer::class]);
10+
};

src/Config/Level/DocblockLevel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use PhpCsFixer\Fixer\Phpdoc\PhpdocTrimFixer;
2929
use PhpCsFixer\Fixer\Phpdoc\PhpdocTypesFixer;
3030
use PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer;
31+
use Symplify\CodingStandard\Fixer\Annotation\RemoveEventSubscriberDescriptionFixer;
3132
use Symplify\CodingStandard\Fixer\Annotation\RemoveMethodNameDuplicateDescriptionFixer;
3233
use Symplify\CodingStandard\Fixer\Annotation\RemovePHPStormAnnotationFixer;
3334
use Symplify\CodingStandard\Fixer\Annotation\RemovePropertyVariableNameDescriptionFixer;
@@ -114,6 +115,7 @@ final class DocblockLevel
114115
RemovePHPStormAnnotationFixer::class,
115116
RemoveMethodNameDuplicateDescriptionFixer::class,
116117
RemovePropertyVariableNameDescriptionFixer::class,
118+
RemoveEventSubscriberDescriptionFixer::class,
117119
];
118120

119121
/**

0 commit comments

Comments
 (0)