diff --git a/src/Rules/DocBlockHeaderFixer.php b/src/Rules/DocBlockHeaderFixer.php index d3619c5..af354bb 100644 --- a/src/Rules/DocBlockHeaderFixer.php +++ b/src/Rules/DocBlockHeaderFixer.php @@ -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; } } @@ -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; } diff --git a/tests/src/Rules/DocBlockHeaderFixerTest.php b/tests/src/Rules/DocBlockHeaderFixerTest.php index b967c91..fe8f34e 100644 --- a/tests/src/Rules/DocBlockHeaderFixerTest.php +++ b/tests/src/Rules/DocBlockHeaderFixerTest.php @@ -534,6 +534,73 @@ public function testFindInsertPositionWithAttribute(): void self::assertSame(1, $result); } + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 8.2')] + public function testFindInsertPositionWithReadonlyModifier(): void + { + $code = '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 = '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 = '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 = "generateCode()); + } + public function testBuildDocBlockWithClassName(): void { $method = new ReflectionMethod($this->fixer, 'buildDocBlock');