Skip to content

Commit 1cb2cbd

Browse files
committed
feat: add option to include class name in DocBlock generation
1 parent fe26f8a commit 1cb2cbd

File tree

5 files changed

+349
-32
lines changed

5 files changed

+349
-32
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ return (new PhpCsFixer\Config())
7575
],
7676
'preserve_existing' => true,
7777
'separate' => 'none',
78+
'add_class_name' => true,
7879
],
7980
])
8081
;
@@ -98,7 +99,8 @@ return (new PhpCsFixer\Config())
9899
'package' => 'PhpDocBlockHeaderFixer',
99100
],
100101
preserveExisting: true,
101-
separate: \KonradMichalik\PhpDocBlockHeaderFixer\Model\Separate::None
102+
separate: \KonradMichalik\PhpDocBlockHeaderFixer\Enum\Separate::None,
103+
addClassName: true
102104
)->__toArray()
103105
])
104106
;

src/Generators/DocBlockHeader.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +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,
3839
) {}
3940

4041
/**
@@ -44,10 +45,11 @@ public static function create(
4445
array $annotations,
4546
bool $preserveExisting = true,
4647
Separate $separate = Separate::Both,
48+
bool $addClassName = false,
4749
): self {
4850
self::validateAnnotations($annotations);
4951

50-
return new self($annotations, $preserveExisting, $separate);
52+
return new self($annotations, $preserveExisting, $separate, $addClassName);
5153
}
5254

5355
/**
@@ -60,6 +62,7 @@ public function __toArray(): array
6062
'annotations' => $this->annotations,
6163
'preserve_existing' => $this->preserveExisting,
6264
'separate' => $this->separate->value,
65+
'add_class_name' => $this->addClassName,
6366
],
6467
];
6568
}

src/Rules/DocBlockHeaderFixer.php

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ public function getConfigurationDefinition(): FixerConfigurationResolverInterfac
8181
->setAllowedValues(Separate::getList())
8282
->setDefault(Separate::Both->value)
8383
->getOption(),
84+
(new FixerOptionBuilder('add_class_name', 'Add class name before annotations'))
85+
->setAllowedTypes(['bool'])
86+
->setDefault(false)
87+
->getOption(),
8488
]);
8589
}
8690

@@ -103,27 +107,50 @@ protected function applyFix(SplFileInfo $file, Tokens $tokens): void
103107
continue;
104108
}
105109

106-
$this->processClassDocBlock($tokens, $index, $annotations);
110+
$className = $this->getClassName($tokens, $index);
111+
$this->processClassDocBlock($tokens, $index, $annotations, $className);
107112
}
108113
}
109114

110115
/**
111116
* @param array<string, string|array<string>> $annotations
112117
*/
113-
private function processClassDocBlock(Tokens $tokens, int $classIndex, array $annotations): void
118+
private function processClassDocBlock(Tokens $tokens, int $classIndex, array $annotations, string $className): void
114119
{
115120
$existingDocBlockIndex = $this->findExistingDocBlock($tokens, $classIndex);
116121
$preserveExisting = $this->resolvedConfiguration['preserve_existing'] ?? true;
117122

118123
if (null !== $existingDocBlockIndex) {
119124
if ($preserveExisting) {
120-
$this->mergeWithExistingDocBlock($tokens, $existingDocBlockIndex, $annotations);
125+
$this->mergeWithExistingDocBlock($tokens, $existingDocBlockIndex, $annotations, $className);
121126
} else {
122-
$this->replaceDocBlock($tokens, $existingDocBlockIndex, $annotations);
127+
$this->replaceDocBlock($tokens, $existingDocBlockIndex, $annotations, $className);
123128
}
124129
} else {
125-
$this->insertNewDocBlock($tokens, $classIndex, $annotations);
130+
$this->insertNewDocBlock($tokens, $classIndex, $annotations, $className);
131+
}
132+
}
133+
134+
private function getClassName(Tokens $tokens, int $classIndex): string
135+
{
136+
// Look for the class name token after the 'class' keyword
137+
for ($i = $classIndex + 1, $limit = $tokens->count(); $i < $limit; ++$i) {
138+
$token = $tokens[$i];
139+
140+
if ($token->isWhitespace()) {
141+
continue;
142+
}
143+
144+
// The first non-whitespace token after 'class' should be the class name
145+
if ($token->isGivenKind(T_STRING)) {
146+
return $token->getContent();
147+
}
148+
149+
// If we hit anything else, stop looking
150+
break;
126151
}
152+
153+
return '';
127154
}
128155

129156
private function findExistingDocBlock(Tokens $tokens, int $classIndex): ?int
@@ -151,29 +178,29 @@ private function findExistingDocBlock(Tokens $tokens, int $classIndex): ?int
151178
/**
152179
* @param array<string, string|array<string>> $annotations
153180
*/
154-
private function mergeWithExistingDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations): void
181+
private function mergeWithExistingDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $className): void
155182
{
156183
$existingContent = $tokens[$docBlockIndex]->getContent();
157184
$existingAnnotations = $this->parseExistingAnnotations($existingContent);
158185
$mergedAnnotations = $this->mergeAnnotations($existingAnnotations, $annotations);
159186

160-
$newDocBlock = $this->buildDocBlock($mergedAnnotations);
187+
$newDocBlock = $this->buildDocBlock($mergedAnnotations, $className);
161188
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
162189
}
163190

164191
/**
165192
* @param array<string, string|array<string>> $annotations
166193
*/
167-
private function replaceDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations): void
194+
private function replaceDocBlock(Tokens $tokens, int $docBlockIndex, array $annotations, string $className): void
168195
{
169-
$newDocBlock = $this->buildDocBlock($annotations);
196+
$newDocBlock = $this->buildDocBlock($annotations, $className);
170197
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $newDocBlock]);
171198
}
172199

173200
/**
174201
* @param array<string, string|array<string>> $annotations
175202
*/
176-
private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annotations): void
203+
private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annotations, string $className): void
177204
{
178205
$separate = $this->resolvedConfiguration['separate'] ?? 'both';
179206
$insertIndex = $this->findInsertPosition($tokens, $classIndex);
@@ -186,7 +213,7 @@ private function insertNewDocBlock(Tokens $tokens, int $classIndex, array $annot
186213
}
187214

188215
// Add the DocBlock
189-
$docBlock = $this->buildDocBlock($annotations);
216+
$docBlock = $this->buildDocBlock($annotations, $className);
190217
$tokensToInsert[] = new Token([T_DOC_COMMENT, $docBlock]);
191218

192219
// Add separation after comment if needed
@@ -255,14 +282,26 @@ private function mergeAnnotations(array $existing, array $new): array
255282
/**
256283
* @param array<string, string|array<string>> $annotations
257284
*/
258-
private function buildDocBlock(array $annotations): string
285+
private function buildDocBlock(array $annotations, string $className): string
259286
{
260-
if (empty($annotations)) {
287+
$addClassName = $this->resolvedConfiguration['add_class_name'] ?? false;
288+
289+
if (empty($annotations) && !$addClassName) {
261290
return "/**\n */";
262291
}
263292

264293
$docBlock = "/**\n";
265294

295+
// Add class name with dot if configured
296+
if ($addClassName && !empty($className)) {
297+
$docBlock .= " * {$className}.\n";
298+
299+
// Add empty line after class name if there are annotations
300+
if (!empty($annotations)) {
301+
$docBlock .= " *\n";
302+
}
303+
}
304+
266305
foreach ($annotations as $tag => $value) {
267306
if (empty($value)) {
268307
$docBlock .= " * @{$tag}\n";

tests/src/Generators/DocBlockHeaderTest.php

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

@@ -109,6 +110,7 @@ public function testToArrayWithDefaultParameters(): void
109110
'annotations' => $annotations,
110111
'preserve_existing' => true,
111112
'separate' => 'both',
113+
'add_class_name' => false,
112114
],
113115
];
114116

@@ -268,4 +270,44 @@ public function testClassIsFinal(): void
268270

269271
self::assertTrue($reflection->isFinal());
270272
}
273+
274+
public function testCreateWithAddClassName(): void
275+
{
276+
$annotations = ['author' => 'John Doe'];
277+
$docBlockHeader = DocBlockHeader::create(
278+
$annotations,
279+
true,
280+
Separate::None,
281+
true,
282+
);
283+
284+
self::assertSame($annotations, $docBlockHeader->annotations);
285+
self::assertTrue($docBlockHeader->preserveExisting);
286+
self::assertSame(Separate::None, $docBlockHeader->separate);
287+
self::assertTrue($docBlockHeader->addClassName);
288+
}
289+
290+
public function testToArrayWithAddClassName(): void
291+
{
292+
$annotations = ['author' => 'John Doe'];
293+
$docBlockHeader = DocBlockHeader::create(
294+
$annotations,
295+
false,
296+
Separate::Top,
297+
true,
298+
);
299+
300+
$result = $docBlockHeader->__toArray();
301+
302+
$expected = [
303+
'KonradMichalik/docblock_header_comment' => [
304+
'annotations' => $annotations,
305+
'preserve_existing' => false,
306+
'separate' => 'top',
307+
'add_class_name' => true,
308+
],
309+
];
310+
311+
self::assertSame($expected, $result);
312+
}
271313
}

0 commit comments

Comments
 (0)