1616use PhpCsFixer \FixerDefinition \FixerDefinitionInterface ;
1717use PhpCsFixer \Indicator \PhpUnitTestCaseIndicator ;
1818use PhpCsFixer \Preg ;
19+ use PhpCsFixer \Tokenizer \Analyzer \Analysis \NamespaceUseAnalysis ;
20+ use PhpCsFixer \Tokenizer \Analyzer \AttributeAnalyzer ;
21+ use PhpCsFixer \Tokenizer \Analyzer \FullyQualifiedNameAnalyzer ;
22+ use PhpCsFixer \Tokenizer \CT ;
1923use PhpCsFixer \Tokenizer \Token ;
2024use PhpCsFixer \Tokenizer \Tokens ;
2125
@@ -84,14 +88,24 @@ private function fixClass(Tokens $tokens, int $index, int $endIndex): void
8488
8589 private static function fixMethod (Tokens $ tokens , int $ index ): void
8690 {
87- $ phpDocIndex = $ tokens ->getPrevTokenOfKind ($ index , ['; ' , [\T_DOC_COMMENT ]]);
88- if ($ phpDocIndex === null || ! $ tokens [$ phpDocIndex ]->isGivenKind (\ T_DOC_COMMENT )) {
91+ $ index = $ tokens ->getPrevTokenOfKind ($ index , ['; ' , [\T_DOC_COMMENT ], [ CT :: T_ATTRIBUTE_CLOSE ]]);
92+ if ($ index === null || $ tokens [$ index ]->equals ( ' ; ' )) {
8993 return ;
9094 }
9195
92- $ tokens [$ phpDocIndex ] = new Token ([
93- \T_DOC_COMMENT ,
96+ if ($ tokens [$ index ]->isGivenKind (\T_DOC_COMMENT )) {
97+ self ::fixPhpDoc ($ tokens , $ index );
98+ }
99+
100+ if ($ tokens [$ index ]->isGivenKind (CT ::T_ATTRIBUTE_CLOSE )) {
101+ self ::fixAttribute ($ tokens , $ index );
102+ }
103+ }
94104
105+ private static function fixPhpDoc (Tokens $ tokens , int $ index ): void
106+ {
107+ $ tokens [$ index ] = new Token ([
108+ \T_DOC_COMMENT ,
95109 Preg::replaceCallback (
96110 '/(@requires \\s+ \\S+ \\s+)(.+?)( \\s*)$/m ' ,
97111 static function (array $ matches ): string {
@@ -104,8 +118,49 @@ static function (array $matches): string {
104118
105119 return $ matches [1 ] . $ matches [2 ] . $ matches [3 ];
106120 },
107- $ tokens [$ phpDocIndex ]->getContent (),
121+ $ tokens [$ index ]->getContent (),
108122 ),
109123 ]);
110124 }
125+
126+ private static function fixAttribute (Tokens $ tokens , int $ index ): void
127+ {
128+ $ fullyQualifiedNameAnalyzer = new FullyQualifiedNameAnalyzer ($ tokens );
129+ foreach (AttributeAnalyzer::collect ($ tokens , $ tokens ->findBlockStart (Tokens::BLOCK_TYPE_ATTRIBUTE , $ index )) as $ attributeAnalysis ) {
130+ foreach ($ attributeAnalysis ->getAttributes () as $ attribute ) {
131+ $ attributeName = \strtolower ($ fullyQualifiedNameAnalyzer ->getFullyQualifiedName ($ attribute ['name ' ], $ attribute ['start ' ], NamespaceUseAnalysis::TYPE_CLASS ));
132+ if (
133+ $ attributeName === 'phpunit \\framework \\attributes \\requiresphp '
134+ || $ attributeName === 'phpunit \\framework \\attributes \\requiresphpunit '
135+ ) {
136+ $ stringIndex = $ tokens ->getPrevMeaningfulToken ($ attribute ['end ' ]);
137+ \assert (\is_int ($ stringIndex ));
138+ if (!$ tokens [$ stringIndex ]->isGivenKind (\T_CONSTANT_ENCAPSED_STRING )) {
139+ continue ;
140+ }
141+
142+ $ openParenthesisIndex = $ tokens ->getPrevMeaningfulToken ($ stringIndex );
143+ \assert (\is_int ($ openParenthesisIndex ));
144+ if (!$ tokens [$ openParenthesisIndex ]->equals ('( ' )) {
145+ continue ;
146+ }
147+
148+ $ quote = \substr ($ tokens [$ stringIndex ]->getContent (), -1 , 1 );
149+ $ tokens [$ stringIndex ] = new Token ([
150+ \T_CONSTANT_ENCAPSED_STRING ,
151+ $ quote . self ::fixString (\substr ($ tokens [$ stringIndex ]->getContent (), 1 , -1 )) . $ quote ,
152+ ]);
153+ }
154+ }
155+ }
156+ }
157+
158+ private static function fixString (string $ string ): string
159+ {
160+ if (Preg::match ('/^[ \\d \\.-]+(dev|(RC|alpha|beta)[ \\d \\.])?$/ ' , $ string )) {
161+ return '>= ' . $ string ;
162+ }
163+
164+ return $ string ;
165+ }
111166}
0 commit comments