Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Rules/DocBlockHeaderFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private function findExistingDocBlock(Tokens $tokens, int $structureIndex): ?int
}

// If we hit any other meaningful token (except modifiers), stop looking
if (!$token->isGivenKind([\T_FINAL, \T_ABSTRACT, \T_ATTRIBUTE])) {
if (!$token->isGivenKind([\T_FINAL, \T_ABSTRACT, \T_READONLY, \T_ATTRIBUTE])) {
break;
}
}
Expand Down Expand Up @@ -315,7 +315,7 @@ private function findInsertPosition(Tokens $tokens, int $structureIndex): int
continue;
}

if ($token->isGivenKind([\T_FINAL, \T_ABSTRACT, \T_ATTRIBUTE])) {
if ($token->isGivenKind([\T_FINAL, \T_ABSTRACT, \T_READONLY, \T_ATTRIBUTE])) {
$insertIndex = $i;
continue;
}
Expand Down
67 changes: 67 additions & 0 deletions tests/src/Rules/DocBlockHeaderFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,73 @@ public function testFindInsertPositionWithAttribute(): void
self::assertSame(1, $result);
}

#[\PHPUnit\Framework\Attributes\RequiresPhp('>= 8.2')]
public function testFindInsertPositionWithReadonlyModifier(): void
{
$code = '<?php readonly class Foo {}';
$tokens = Tokens::fromCode($code);

$method = new ReflectionMethod($this->fixer, 'findInsertPosition');

// Find the class token index
$classIndex = null;
for ($i = 0; $i < $tokens->count(); ++$i) {
if ($tokens[$i]->isGivenKind(\T_CLASS)) {
$classIndex = $i;
break;
}
}

$result = $method->invoke($this->fixer, $tokens, $classIndex);

// Should find the readonly modifier at index 1
self::assertSame(1, $result);
}

#[\PHPUnit\Framework\Attributes\RequiresPhp('>= 8.2')]
public function testFindInsertPositionWithFinalReadonlyModifiers(): void
{
$code = '<?php final readonly class Foo {}';
$tokens = Tokens::fromCode($code);

$method = new ReflectionMethod($this->fixer, 'findInsertPosition');

// Find the class token index
$classIndex = null;
for ($i = 0; $i < $tokens->count(); ++$i) {
if ($tokens[$i]->isGivenKind(\T_CLASS)) {
$classIndex = $i;
break;
}
}

$result = $method->invoke($this->fixer, $tokens, $classIndex);

// Should find the final modifier at index 1 (the first modifier)
self::assertSame(1, $result);
}

#[\PHPUnit\Framework\Attributes\RequiresPhp('>= 8.2')]
public function testApplyFixAddsDocBlockToFinalReadonlyClass(): void
{
$code = '<?php final readonly class Foo {}';
$tokens = Tokens::fromCode($code);
$file = new SplFileInfo(__FILE__);

$method = new ReflectionMethod($this->fixer, 'applyFix');

$this->fixer->configure([
'annotations' => ['author' => 'John Doe'],
'separate' => 'none',
'ensure_spacing' => false,
]);
$method->invoke($this->fixer, $file, $tokens);

// DocBlock should be placed BEFORE final, not between readonly and class
$expected = "<?php /**\n * @author John Doe\n */final readonly class Foo {}";
self::assertSame($expected, $tokens->generateCode());
}

public function testBuildDocBlockWithClassName(): void
{
$method = new ReflectionMethod($this->fixer, 'buildDocBlock');
Expand Down
Loading