Skip to content

Commit a0a5fb6

Browse files
committed
feat: rename 'addClassName' to 'addStructureName' and support more php structures for DocBlock generation
1 parent 1b062d8 commit a0a5fb6

File tree

5 files changed

+221
-70
lines changed

5 files changed

+221
-70
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
</div>
1111

12-
This packages contains a [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) rule to automatically fix the class header regarding PHP DocBlocks.
12+
This packages contains a [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) rule to automatically fix the header regarding PHP DocBlocks for classes, interfaces, traits and enums.
1313

1414
> [!warning]
1515
> This package is in early development stage and may change significantly in the future. Use it at your own risk.
@@ -26,6 +26,10 @@ class MyClass
2626
// ...
2727
}
2828
}
29+
30+
interface MyInterface {}
31+
trait MyTrait {}
32+
enum MyEnum {}
2933
```
3034

3135
**After:**
@@ -76,7 +80,7 @@ return (new PhpCsFixer\Config())
7680
],
7781
'preserve_existing' => true,
7882
'separate' => 'none',
79-
'add_class_name' => true,
83+
'add_structure_name' => true,
8084
],
8185
])
8286
;
@@ -100,7 +104,7 @@ return (new PhpCsFixer\Config())
100104
],
101105
preserveExisting: true,
102106
separate: \KonradMichalik\PhpDocBlockHeaderFixer\Enum\Separate::None,
103-
addClassName: true
107+
addStructureName: true
104108
)->__toArray()
105109
])
106110
;

src/Generators/DocBlockHeader.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private function __construct(
3535
public readonly array $annotations,
3636
public readonly bool $preserveExisting,
3737
public readonly Separate $separate,
38-
public readonly bool $addClassName,
38+
public readonly bool $addStructureName,
3939
) {}
4040

4141
/**
@@ -45,11 +45,11 @@ public static function create(
4545
array $annotations,
4646
bool $preserveExisting = true,
4747
Separate $separate = Separate::Both,
48-
bool $addClassName = false,
48+
bool $addStructureName = false,
4949
): self {
5050
self::validateAnnotations($annotations);
5151

52-
return new self($annotations, $preserveExisting, $separate, $addClassName);
52+
return new self($annotations, $preserveExisting, $separate, $addStructureName);
5353
}
5454

5555
/**
@@ -62,7 +62,7 @@ public function __toArray(): array
6262
'annotations' => $this->annotations,
6363
'preserve_existing' => $this->preserveExisting,
6464
'separate' => $this->separate->value,
65-
'add_class_name' => $this->addClassName,
65+
'add_structure_name' => $this->addStructureName,
6666
],
6767
];
6868
}

src/Rules/DocBlockHeaderFixer.php

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ final class DocBlockHeaderFixer extends AbstractFixer implements ConfigurableFix
5151
public function getDefinition(): FixerDefinitionInterface
5252
{
5353
return new FixerDefinition(
54-
'Add configurable DocBlock annotations before class declarations.',
54+
'Add configurable DocBlock annotations before class, interface, trait, and enum declarations.',
5555
[],
5656
);
5757
}
@@ -63,7 +63,10 @@ public function getName(): string
6363

6464
public function isCandidate(Tokens $tokens): bool
6565
{
66-
return $tokens->isTokenKindFound(T_CLASS);
66+
return $tokens->isTokenKindFound(T_CLASS)
67+
|| $tokens->isTokenKindFound(T_INTERFACE)
68+
|| $tokens->isTokenKindFound(T_TRAIT)
69+
|| $tokens->isTokenKindFound(T_ENUM);
6770
}
6871

6972
public function getConfigurationDefinition(): FixerConfigurationResolverInterface
@@ -81,7 +84,7 @@ public function getConfigurationDefinition(): FixerConfigurationResolverInterfac
8184
->setAllowedValues(Separate::getList())
8285
->setDefault(Separate::None->value)
8386
->getOption(),
84-
(new FixerOptionBuilder('add_class_name', 'Add class name before annotations'))
87+
(new FixerOptionBuilder('add_structure_name', 'Add structure name before annotations'))
8588
->setAllowedTypes(['bool'])
8689
->setDefault(false)
8790
->getOption(),
@@ -103,45 +106,45 @@ protected function applyFix(SplFileInfo $file, Tokens $tokens): void
103106
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
104107
$token = $tokens[$index];
105108

106-
if (!$token->isGivenKind(T_CLASS)) {
109+
if (!$token->isGivenKind([T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM])) {
107110
continue;
108111
}
109112

110-
$className = $this->getClassName($tokens, $index);
111-
$this->processClassDocBlock($tokens, $index, $annotations, $className);
113+
$structureName = $this->getStructureName($tokens, $index);
114+
$this->processStructureDocBlock($tokens, $index, $annotations, $structureName);
112115
}
113116
}
114117

115118
/**
116119
* @param array<string, string|array<string>> $annotations
117120
*/
118-
private function processClassDocBlock(Tokens $tokens, int $classIndex, array $annotations, string $className): void
121+
private function processStructureDocBlock(Tokens $tokens, int $structureIndex, array $annotations, string $structureName): void
119122
{
120-
$existingDocBlockIndex = $this->findExistingDocBlock($tokens, $classIndex);
123+
$existingDocBlockIndex = $this->findExistingDocBlock($tokens, $structureIndex);
121124
$preserveExisting = $this->resolvedConfiguration['preserve_existing'] ?? true;
122125

123126
if (null !== $existingDocBlockIndex) {
124127
if ($preserveExisting) {
125-
$this->mergeWithExistingDocBlock($tokens, $existingDocBlockIndex, $annotations, $className);
128+
$this->mergeWithExistingDocBlock($tokens, $existingDocBlockIndex, $annotations, $structureName);
126129
} else {
127-
$this->replaceDocBlock($tokens, $existingDocBlockIndex, $annotations, $className);
130+
$this->replaceDocBlock($tokens, $existingDocBlockIndex, $annotations, $structureName);
128131
}
129132
} else {
130-
$this->insertNewDocBlock($tokens, $classIndex, $annotations, $className);
133+
$this->insertNewDocBlock($tokens, $structureIndex, $annotations, $structureName);
131134
}
132135
}
133136

134-
private function getClassName(Tokens $tokens, int $classIndex): string
137+
private function getStructureName(Tokens $tokens, int $structureIndex): string
135138
{
136-
// Look for the class name token after the 'class' keyword
137-
for ($i = $classIndex + 1, $limit = $tokens->count(); $i < $limit; ++$i) {
139+
// Look for the structure name token after the keyword (class/interface/trait/enum)
140+
for ($i = $structureIndex + 1, $limit = $tokens->count(); $i < $limit; ++$i) {
138141
$token = $tokens[$i];
139142

140143
if ($token->isWhitespace()) {
141144
continue;
142145
}
143146

144-
// The first non-whitespace token after 'class' should be the class name
147+
// The first non-whitespace token after the keyword should be the structure name
145148
if ($token->isGivenKind(T_STRING)) {
146149
return $token->getContent();
147150
}
@@ -153,9 +156,9 @@ private function getClassName(Tokens $tokens, int $classIndex): string
153156
return '';
154157
}
155158

156-
private function findExistingDocBlock(Tokens $tokens, int $classIndex): ?int
159+
private function findExistingDocBlock(Tokens $tokens, int $structureIndex): ?int
157160
{
158-
for ($i = $classIndex - 1; $i >= 0; --$i) {
161+
for ($i = $structureIndex - 1; $i >= 0; --$i) {
159162
$token = $tokens[$i];
160163

161164
if ($token->isWhitespace()) {
@@ -178,32 +181,32 @@ private function findExistingDocBlock(Tokens $tokens, int $classIndex): ?int
178181
/**
179182
* @param array<string, string|array<string>> $annotations
180183
*/
181-
private function mergeWithExistingDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $className): void
184+
private function mergeWithExistingDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $structureName): void
182185
{
183186
$existingContent = $tokens[$docBlockIndex]->getContent();
184187
$existingAnnotations = $this->parseExistingAnnotations($existingContent);
185188
$mergedAnnotations = $this->mergeAnnotations($existingAnnotations, $annotations);
186189

187-
$newDocBlock = $this->buildDocBlock($mergedAnnotations, $className);
190+
$newDocBlock = $this->buildDocBlock($mergedAnnotations, $structureName);
188191
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
189192
}
190193

191194
/**
192195
* @param array<string, string|array<string>> $annotations
193196
*/
194-
private function replaceDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $className): void
197+
private function replaceDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $structureName): void
195198
{
196-
$newDocBlock = $this->buildDocBlock($annotations, $className);
199+
$newDocBlock = $this->buildDocBlock($annotations, $structureName);
197200
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
198201
}
199202

200203
/**
201204
* @param array<string, string|array<string>> $annotations
202205
*/
203-
private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annotations, string $className): void
206+
private function insertNewDocBlock(Tokens $tokens, int $structureIndex, array $annotations, string $structureName): void
204207
{
205208
$separate = $this->resolvedConfiguration['separate'] ?? 'none';
206-
$insertIndex = $this->findInsertPosition($tokens, $classIndex);
209+
$insertIndex = $this->findInsertPosition($tokens, $structureIndex);
207210

208211
$tokensToInsert = [];
209212

@@ -213,14 +216,14 @@ private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annot
213216
}
214217

215218
// Add the DocBlock
216-
$docBlock = $this->buildDocBlock($annotations, $className);
219+
$docBlock = $this->buildDocBlock($annotations, $structureName);
217220
$tokensToInsert[] = new Token([T_DOC_COMMENT, $docBlock]);
218221

219222
// For compatibility with no_blank_lines_after_phpdoc, only add bottom separation when 'separate' is not 'none'
220223
// This prevents conflicts with PHP-CS-Fixer rules that manage DocBlock spacing
221224
if (in_array($separate, ['bottom', 'both'], true)) {
222-
// Check if there's already whitespace after the class declaration
223-
$nextToken = $tokens[$classIndex] ?? null;
225+
// Check if there's already whitespace after the structure declaration
226+
$nextToken = $tokens[$structureIndex] ?? null;
224227
if (null !== $nextToken && !$nextToken->isWhitespace()) {
225228
$tokensToInsert[] = new Token([T_WHITESPACE, "\n"]);
226229
}
@@ -229,12 +232,12 @@ private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annot
229232
$tokens->insertAt($insertIndex, $tokensToInsert);
230233
}
231234

232-
private function findInsertPosition(Tokens $tokens, int $classIndex): int
235+
private function findInsertPosition(Tokens $tokens, int $structureIndex): int
233236
{
234-
$insertIndex = $classIndex;
237+
$insertIndex = $structureIndex;
235238

236239
// Look backwards for attributes, final, abstract keywords
237-
for ($i = $classIndex - 1; $i >= 0; --$i) {
240+
for ($i = $structureIndex - 1; $i >= 0; --$i) {
238241
$token = $tokens[$i];
239242

240243
if ($token->isWhitespace()) {
@@ -287,21 +290,21 @@ private function mergeAnnotations(array $existing, array $new): array
287290
/**
288291
* @param array<string, string|array<string>> $annotations
289292
*/
290-
private function buildDocBlock(array $annotations, string $className): string
293+
private function buildDocBlock(array $annotations, string $structureName): string
291294
{
292-
$addClassName = $this->resolvedConfiguration['add_class_name'] ?? false;
295+
$addStructureName = $this->resolvedConfiguration['add_structure_name'] ?? false;
293296

294-
if (empty($annotations) && !$addClassName) {
297+
if (empty($annotations) && !$addStructureName) {
295298
return "/**\n */";
296299
}
297300

298301
$docBlock = "/**\n";
299302

300-
// Add class name with dot if configured
301-
if ($addClassName && !empty($className)) {
302-
$docBlock .= " * {$className}.\n";
303+
// Add structure name with dot if configured
304+
if ($addStructureName && !empty($structureName)) {
305+
$docBlock .= " * {$structureName}.\n";
303306

304-
// Add empty line after class name if there are annotations - compatible with phpdoc_separation
307+
// Add empty line after structure name if there are annotations - compatible with phpdoc_separation
305308
if (!empty($annotations)) {
306309
$docBlock .= " *\n";
307310
}

tests/src/Generators/DocBlockHeaderTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function testToArrayReturnsCorrectStructure(): void
9191
'annotations' => $annotations,
9292
'preserve_existing' => false,
9393
'separate' => 'top',
94-
'add_class_name' => false,
94+
'add_structure_name' => false,
9595
],
9696
];
9797

@@ -110,7 +110,7 @@ public function testToArrayWithDefaultParameters(): void
110110
'annotations' => $annotations,
111111
'preserve_existing' => true,
112112
'separate' => 'both',
113-
'add_class_name' => false,
113+
'add_structure_name' => false,
114114
],
115115
];
116116

@@ -270,7 +270,7 @@ public function testClassIsFinal(): void
270270
self::assertTrue($reflection->isFinal());
271271
}
272272

273-
public function testCreateWithAddClassName(): void
273+
public function testCreateWithAddStructureName(): void
274274
{
275275
$annotations = ['author' => 'John Doe'];
276276
$docBlockHeader = DocBlockHeader::create(
@@ -283,10 +283,10 @@ public function testCreateWithAddClassName(): void
283283
self::assertSame($annotations, $docBlockHeader->annotations);
284284
self::assertTrue($docBlockHeader->preserveExisting);
285285
self::assertSame(Separate::None, $docBlockHeader->separate);
286-
self::assertTrue($docBlockHeader->addClassName);
286+
self::assertTrue($docBlockHeader->addStructureName);
287287
}
288288

289-
public function testToArrayWithAddClassName(): void
289+
public function testToArrayWithAddStructureName(): void
290290
{
291291
$annotations = ['author' => 'John Doe'];
292292
$docBlockHeader = DocBlockHeader::create(
@@ -303,7 +303,7 @@ public function testToArrayWithAddClassName(): void
303303
'annotations' => $annotations,
304304
'preserve_existing' => false,
305305
'separate' => 'top',
306-
'add_class_name' => true,
306+
'add_structure_name' => true,
307307
],
308308
];
309309

0 commit comments

Comments
 (0)