44
55namespace Rector \CodingStyle \Rector \Enum_ ;
66
7+ use PHPStan \BetterReflection \Reflector \DefaultReflector ;
8+ use PHPStan \BetterReflection \Reflector \Exception \IdentifierNotFound ;
9+ use PHPStan \Reflection \ClassReflection ;
710use PhpParser \Node ;
811use PhpParser \Node \Expr \ClassConstFetch ;
912use PhpParser \Node \Identifier ;
1013use PhpParser \Node \Name ;
1114use PhpParser \Node \Stmt \Enum_ ;
1215use PhpParser \Node \Stmt \EnumCase ;
13- use PHPStan \BetterReflection \Reflection \ReflectionEnum ;
14- use PHPStan \BetterReflection \Reflector \DefaultReflector ;
15- use PHPStan \BetterReflection \Reflector \Exception \IdentifierNotFound ;
16- use PHPStan \Reflection \ReflectionProvider ;
1716use Rector \Configuration \Option ;
1817use Rector \Configuration \Parameter \SimpleParameterProvider ;
1918use Rector \NodeTypeResolver \Reflection \BetterReflection \SourceLocatorProvider \DynamicSourceLocatorProvider ;
2423
2524/**
2625 * @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\EnumCaseToPascalCaseRectorTest
26+ * @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\WithAutoloadPathsTest
2727 */
2828final class EnumCaseToPascalCaseRector extends AbstractRector
2929{
3030 public function __construct (
31- private readonly ReflectionProvider $ reflectionProvider ,
3231 private readonly DynamicSourceLocatorProvider $ dynamicSourceLocatorProvider ,
3332 ) {
3433 }
@@ -122,43 +121,54 @@ private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Nod
122121 return null ;
123122 }
124123
125- if ($ this ->nodeTypeResolver ->getType ($ classConstFetch ->class )->isEnum ()->no ()) {
124+ $ constName = $ classConstFetch ->name ->toString ();
125+ $ pascalCaseName = $ this ->convertToPascalCase ($ constName );
126+
127+ // short circuit if already in pascal case
128+ if ($ constName === $ pascalCaseName ) {
126129 return null ;
127130 }
128131
129- $ constName = $ classConstFetch ->name ->toString ();
132+ $ classReflection = $ this ->nodeTypeResolver ->getType ($ classConstFetch ->class )
133+ ->getObjectClassReflections ()[0 ] ?? null ;
134+
135+ if ($ classReflection === null || ! $ classReflection ->isEnum ()) {
136+ return null ;
137+ }
130138
131- // Skip "class" constant
132- if ($ constName === 'class ' ) {
139+ if (! $ this ->isEnumCase ($ classReflection , $ constName , $ pascalCaseName )) {
133140 return null ;
134141 }
135142
136- $ enumClassName = $ classConstFetch ->class ->toString ();
137- if (! $ this ->reflectionProvider ->hasClass ($ enumClassName )) {
143+ if ($ this ->isUsedOutsideOfProject ($ classConstFetch ->class )) {
138144 return null ;
139145 }
140146
147+ $ classConstFetch ->name = new Identifier ($ pascalCaseName );
148+ return $ classConstFetch ;
149+ }
150+
151+ private function isUsedOutsideOfProject (Name $ name ): bool
152+ {
153+ if (in_array ($ name ->toString (), ['self ' , 'static ' ], true )) {
154+ return false ;
155+ }
156+
141157 $ sourceLocator = $ this ->dynamicSourceLocatorProvider ->provide ();
142158 $ defaultReflector = new DefaultReflector ($ sourceLocator );
143159
144160 try {
145- $ classIdentifier = $ defaultReflector ->reflectClass ($ classConstFetch -> class ->toString ());
161+ $ classIdentifier = $ defaultReflector ->reflectClass ($ name ->toString ());
146162 } catch (IdentifierNotFound ) {
147163 // source is outside the paths defined in withPaths(), eg: vendor
148- return null ;
149- }
150-
151- // ensure exactly ReflectionEnum
152- if (! $ classIdentifier instanceof ReflectionEnum) {
153- return null ;
164+ return true ;
154165 }
155166
156- // ensure not part of definition in ->withAutoloadPaths()
157167 $ fileTarget = $ classIdentifier ->getFileName ();
158168
159169 // possibly native
160170 if ($ fileTarget === null ) {
161- return null ;
171+ return true ;
162172 }
163173
164174 $ autoloadPaths = SimpleParameterProvider::provideArrayParameter (Option::AUTOLOAD_PATHS );
@@ -168,21 +178,21 @@ private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Nod
168178 $ normalizedAutoloadPath = PathNormalizer::normalize ($ autoloadPath );
169179
170180 if ($ autoloadPath === $ fileTarget ) {
171- return null ;
181+ return true ;
172182 }
173183
174184 if (str_starts_with ($ normalizedFileTarget , $ normalizedAutoloadPath . '/ ' )) {
175- return null ;
185+ return true ;
176186 }
177187 }
178188
179- $ pascalCaseName = $ this ->convertToPascalCase ($ constName );
180- if ($ constName !== $ pascalCaseName ) {
181- $ classConstFetch ->name = new Identifier ($ pascalCaseName );
182- return $ classConstFetch ;
183- }
189+ return false ;
190+ }
184191
185- return null ;
192+ private function isEnumCase (ClassReflection $ classReflection , string $ name , string $ pascalName ): bool
193+ {
194+ // the enum case might have already been renamed, need to check both
195+ return $ classReflection ->hasEnumCase ($ name ) || $ classReflection ->hasEnumCase ($ pascalName );
186196 }
187197
188198 private function convertToPascalCase (string $ name ): string
0 commit comments