1414use PhpCsFixer \FixerDefinition \CodeSample ;
1515use PhpCsFixer \FixerDefinition \FixerDefinition ;
1616use PhpCsFixer \FixerDefinition \FixerDefinitionInterface ;
17+ use PhpCsFixer \Tokenizer \Analyzer \Analysis \NamespaceUseAnalysis ;
18+ use PhpCsFixer \Tokenizer \Analyzer \NamespaceUsesAnalyzer ;
1719use PhpCsFixer \Tokenizer \Token ;
1820use PhpCsFixer \Tokenizer \Tokens ;
1921
@@ -60,11 +62,21 @@ public function isRisky(): bool
6062
6163 public function fix (\SplFileInfo $ file , Tokens $ tokens ): void
6264 {
63- $ namespaceStartIndex = 0 ;
65+ $ useDeclarations = (new NamespaceUsesAnalyzer ())->getDeclarationsFromTokens ($ tokens );
66+
67+ $ stringableInterfaces = ['stringable ' ];
6468
6569 for ($ index = 1 ; $ index < $ tokens ->count (); $ index ++) {
6670 if ($ tokens [$ index ]->isGivenKind (\T_NAMESPACE )) {
67- $ namespaceStartIndex = $ index ;
71+ $ stringableInterfaces = [];
72+ continue ;
73+ }
74+
75+ if ($ tokens [$ index ]->isGivenKind (\T_USE )) {
76+ $ name = self ::getNameFromUse ($ tokens , $ index , $ useDeclarations );
77+ if ($ name !== null ) {
78+ $ stringableInterfaces [] = $ name ;
79+ }
6880 continue ;
6981 }
7082
@@ -81,14 +93,35 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
8193 continue ;
8294 }
8395
84- if (self ::doesImplementStringable ($ tokens , $ namespaceStartIndex , $ index , $ classStartIndex )) {
96+ if (self ::doesImplementStringable ($ tokens , $ index , $ classStartIndex , $ stringableInterfaces )) {
8597 continue ;
8698 }
8799
88100 self ::addStringableInterface ($ tokens , $ index );
89101 }
90102 }
91103
104+ /**
105+ * @param list<NamespaceUseAnalysis> $useDeclarations
106+ */
107+ private static function getNameFromUse (Tokens $ tokens , int $ index , array $ useDeclarations ): ?string
108+ {
109+ foreach ($ useDeclarations as $ useDeclaration ) {
110+ if ($ useDeclaration ->getStartIndex () !== $ index ) {
111+ continue ;
112+ }
113+
114+ $ lowercasedFullName = \strtolower ($ useDeclaration ->getFullName ());
115+ if ($ lowercasedFullName !== 'stringable ' && $ lowercasedFullName !== '\\stringable ' ) {
116+ return null ;
117+ }
118+
119+ return \strtolower ($ useDeclaration ->getShortName ());
120+ }
121+
122+ return null ;
123+ }
124+
92125 private static function doesHaveToStringMethod (Tokens $ tokens , int $ classStartIndex , int $ classEndIndex ): bool
93126 {
94127 $ index = $ classStartIndex ;
@@ -115,23 +148,25 @@ private static function doesHaveToStringMethod(Tokens $tokens, int $classStartIn
115148 return false ;
116149 }
117150
118- private static function doesImplementStringable (Tokens $ tokens , int $ namespaceStartIndex , int $ classKeywordIndex , int $ classOpenBraceIndex ): bool
119- {
120- $ interfaces = self ::getInterfaces ($ tokens , $ classKeywordIndex , $ classOpenBraceIndex );
121- if ($ interfaces === []) {
151+ /**
152+ * @param list<string> $stringableInterfaces
153+ */
154+ private static function doesImplementStringable (
155+ Tokens $ tokens ,
156+ int $ classKeywordIndex ,
157+ int $ classOpenBraceIndex ,
158+ array $ stringableInterfaces
159+ ): bool {
160+ $ implementedInterfaces = self ::getInterfaces ($ tokens , $ classKeywordIndex , $ classOpenBraceIndex );
161+ if ($ implementedInterfaces === []) {
122162 return false ;
123163 }
124-
125- if (\in_array ('\\stringable ' , $ interfaces , true )) {
126- return true ;
127- }
128-
129- if ($ namespaceStartIndex === 0 && \in_array ('stringable ' , $ interfaces , true )) {
164+ if (\in_array ('\\stringable ' , $ implementedInterfaces , true )) {
130165 return true ;
131166 }
132167
133- foreach (self :: getImports ( $ tokens , $ namespaceStartIndex , $ classKeywordIndex ) as $ import ) {
134- if (\in_array ($ import , $ interfaces , true )) {
168+ foreach ($ stringableInterfaces as $ stringableInterface ) {
169+ if (\in_array ($ stringableInterface , $ implementedInterfaces , true )) {
135170 return true ;
136171 }
137172 }
@@ -170,34 +205,6 @@ private static function getInterfaces(Tokens $tokens, int $classKeywordIndex, in
170205 return $ interfaces ;
171206 }
172207
173- /**
174- * @return iterable<string>
175- */
176- private static function getImports (Tokens $ tokens , int $ namespaceStartIndex , int $ classKeywordIndex ): iterable
177- {
178- for ($ index = $ namespaceStartIndex ; $ index < $ classKeywordIndex ; $ index ++) {
179- if (!$ tokens [$ index ]->isGivenKind (\T_USE )) {
180- continue ;
181- }
182- $ nameIndex = $ tokens ->getNextMeaningfulToken ($ index );
183- \assert (\is_int ($ nameIndex ));
184-
185- if ($ tokens [$ nameIndex ]->isGivenKind (\T_NS_SEPARATOR )) {
186- $ nameIndex = $ tokens ->getNextMeaningfulToken ($ nameIndex );
187- \assert (\is_int ($ nameIndex ));
188- }
189-
190- $ nextIndex = $ tokens ->getNextMeaningfulToken ($ nameIndex );
191- \assert (\is_int ($ nextIndex ));
192- if ($ tokens [$ nextIndex ]->isGivenKind (\T_AS )) {
193- $ nameIndex = $ tokens ->getNextMeaningfulToken ($ nextIndex );
194- \assert (\is_int ($ nameIndex ));
195- }
196-
197- yield \strtolower ($ tokens [$ nameIndex ]->getContent ());
198- }
199- }
200-
201208 private static function addStringableInterface (Tokens $ tokens , int $ classIndex ): void
202209 {
203210 $ implementsIndex = $ tokens ->getNextTokenOfKind ($ classIndex , ['{ ' , [\T_IMPLEMENTS ]]);
0 commit comments