14
14
use PhpCsFixer \FixerDefinition \CodeSample ;
15
15
use PhpCsFixer \FixerDefinition \FixerDefinition ;
16
16
use PhpCsFixer \FixerDefinition \FixerDefinitionInterface ;
17
+ use PhpCsFixer \Tokenizer \Analyzer \Analysis \NamespaceUseAnalysis ;
18
+ use PhpCsFixer \Tokenizer \Analyzer \NamespaceUsesAnalyzer ;
17
19
use PhpCsFixer \Tokenizer \Token ;
18
20
use PhpCsFixer \Tokenizer \Tokens ;
19
21
@@ -60,11 +62,21 @@ public function isRisky(): bool
60
62
61
63
public function fix (\SplFileInfo $ file , Tokens $ tokens ): void
62
64
{
63
- $ namespaceStartIndex = 0 ;
65
+ $ useDeclarations = (new NamespaceUsesAnalyzer ())->getDeclarationsFromTokens ($ tokens );
66
+
67
+ $ stringableInterfaces = ['stringable ' ];
64
68
65
69
for ($ index = 1 ; $ index < $ tokens ->count (); $ index ++) {
66
70
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 ($ index , $ useDeclarations );
77
+ if ($ name !== null ) {
78
+ $ stringableInterfaces [] = $ name ;
79
+ }
68
80
continue ;
69
81
}
70
82
@@ -81,14 +93,36 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
81
93
continue ;
82
94
}
83
95
84
- if (self ::doesImplementStringable ($ tokens , $ namespaceStartIndex , $ index , $ classStartIndex )) {
96
+ if (self ::doesImplementStringable ($ tokens , $ index , $ classStartIndex , $ stringableInterfaces )) {
85
97
continue ;
86
98
}
87
99
88
100
self ::addStringableInterface ($ tokens , $ index );
89
101
}
90
102
}
91
103
104
+ /**
105
+ * @param list<NamespaceUseAnalysis> $useDeclarations
106
+ */
107
+ private static function getNameFromUse (int $ index , array $ useDeclarations ): ?string
108
+ {
109
+ $ uses = \array_filter (
110
+ $ useDeclarations ,
111
+ static fn (NamespaceUseAnalysis $ namespaceUseAnalysis ): bool => $ namespaceUseAnalysis ->getStartIndex () === $ index ,
112
+ );
113
+
114
+ \assert (\count ($ uses ) === 1 );
115
+
116
+ $ useDeclaration = \reset ($ uses );
117
+
118
+ $ lowercasedFullName = \strtolower ($ useDeclaration ->getFullName ());
119
+ if ($ lowercasedFullName !== 'stringable ' && $ lowercasedFullName !== '\\stringable ' ) {
120
+ return null ;
121
+ }
122
+
123
+ return \strtolower ($ useDeclaration ->getShortName ());
124
+ }
125
+
92
126
private static function doesHaveToStringMethod (Tokens $ tokens , int $ classStartIndex , int $ classEndIndex ): bool
93
127
{
94
128
$ index = $ classStartIndex ;
@@ -115,23 +149,25 @@ private static function doesHaveToStringMethod(Tokens $tokens, int $classStartIn
115
149
return false ;
116
150
}
117
151
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 === []) {
152
+ /**
153
+ * @param list<string> $stringableInterfaces
154
+ */
155
+ private static function doesImplementStringable (
156
+ Tokens $ tokens ,
157
+ int $ classKeywordIndex ,
158
+ int $ classOpenBraceIndex ,
159
+ array $ stringableInterfaces
160
+ ): bool {
161
+ $ implementedInterfaces = self ::getInterfaces ($ tokens , $ classKeywordIndex , $ classOpenBraceIndex );
162
+ if ($ implementedInterfaces === []) {
122
163
return false ;
123
164
}
124
-
125
- if (\in_array ('\\stringable ' , $ interfaces , true )) {
165
+ if (\in_array ('\\stringable ' , $ implementedInterfaces , true )) {
126
166
return true ;
127
167
}
128
168
129
- if ($ namespaceStartIndex === 0 && \in_array ('stringable ' , $ interfaces , true )) {
130
- return true ;
131
- }
132
-
133
- foreach (self ::getImports ($ tokens , $ namespaceStartIndex , $ classKeywordIndex ) as $ import ) {
134
- if (\in_array ($ import , $ interfaces , true )) {
169
+ foreach ($ stringableInterfaces as $ stringableInterface ) {
170
+ if (\in_array ($ stringableInterface , $ implementedInterfaces , true )) {
135
171
return true ;
136
172
}
137
173
}
@@ -170,34 +206,6 @@ private static function getInterfaces(Tokens $tokens, int $classKeywordIndex, in
170
206
return $ interfaces ;
171
207
}
172
208
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
-
201
209
private static function addStringableInterface (Tokens $ tokens , int $ classIndex ): void
202
210
{
203
211
$ implementsIndex = $ tokens ->getNextTokenOfKind ($ classIndex , ['{ ' , [\T_IMPLEMENTS ]]);
0 commit comments