33declare (strict_types=1 );
44namespace Rector \Renaming \Rector \Name ;
55
6+ use PHPStan \Reflection \ReflectionProvider ;
7+ use PhpParser \Node \Identifier ;
68use PhpParser \Node ;
9+ use PhpParser \Node \Expr \ClassConstFetch ;
710use PhpParser \Node \FunctionLike ;
811use PhpParser \Node \Name \FullyQualified ;
912use PhpParser \Node \Stmt \ClassLike ;
1013use PhpParser \Node \Stmt \Expression ;
1114use PhpParser \Node \Stmt \If_ ;
1215use PhpParser \Node \Stmt \Property ;
16+ use PhpParser \NodeVisitor ;
1317use Rector \Configuration \RenamedClassesDataCollector ;
1418use Rector \Contract \Rector \ConfigurableRectorInterface ;
1519use Rector \NodeTypeResolver \Node \AttributeKey ;
@@ -31,10 +35,19 @@ final class RenameClassRector extends AbstractRector implements ConfigurableRect
3135 * @readonly
3236 */
3337 private ClassRenamer $ classRenamer ;
34- public function __construct (RenamedClassesDataCollector $ renamedClassesDataCollector , ClassRenamer $ classRenamer )
38+ /**
39+ * @readonly
40+ */
41+ private ReflectionProvider $ reflectionProvider ;
42+ /**
43+ * @var string
44+ */
45+ public const SKIPPED_AS_CLASS_CONST_FETCH_CLASS = 'skipped_as_class_const_fetch_class ' ;
46+ public function __construct (RenamedClassesDataCollector $ renamedClassesDataCollector , ClassRenamer $ classRenamer , ReflectionProvider $ reflectionProvider )
3547 {
3648 $ this ->renamedClassesDataCollector = $ renamedClassesDataCollector ;
3749 $ this ->classRenamer = $ classRenamer ;
50+ $ this ->reflectionProvider = $ reflectionProvider ;
3851 }
3952 public function getRuleDefinition () : RuleDefinition
4053 {
@@ -69,14 +82,37 @@ function someFunction(SomeNewClass $someOldClass): SomeNewClass
6982 */
7083 public function getNodeTypes () : array
7184 {
72- return [FullyQualified::class, Property::class, FunctionLike::class, Expression::class, ClassLike::class, If_::class];
85+ return [ClassConstFetch::class, FullyQualified::class, Property::class, FunctionLike::class, Expression::class, ClassLike::class, If_::class];
7386 }
7487 /**
75- * @param FunctionLike|FullyQualified|ClassLike|Expression|Property|If_ $node
88+ * @param ClassConstFetch|FunctionLike|FullyQualified|ClassLike|Expression|Property|If_ $node
89+ * @return int|null|\PhpParser\Node
7690 */
77- public function refactor (Node $ node ) : ? Node
91+ public function refactor (Node $ node )
7892 {
7993 $ oldToNewClasses = $ this ->renamedClassesDataCollector ->getOldToNewClasses ();
94+ if ($ node instanceof ClassConstFetch) {
95+ if ($ node ->class instanceof FullyQualified && $ node ->name instanceof Identifier && $ this ->reflectionProvider ->hasClass ($ node ->class ->toString ())) {
96+ foreach ($ oldToNewClasses as $ oldClass => $ newClass ) {
97+ if ($ this ->isName ($ node ->class , $ oldClass ) && $ this ->reflectionProvider ->hasClass ($ newClass )) {
98+ $ classReflection = $ this ->reflectionProvider ->getClass ($ newClass );
99+ if (!$ classReflection ->isInterface ()) {
100+ continue ;
101+ }
102+ $ oldClassReflection = $ this ->reflectionProvider ->getClass ($ oldClass );
103+ if ($ oldClassReflection ->hasConstant ($ node ->name ->toString ()) && !$ classReflection ->hasConstant ($ node ->name ->toString ())) {
104+ // used by ClassRenamer::renameNode()
105+ // that called on ClassRenamingPostRector PostRector
106+ $ node ->class ->setAttribute (self ::SKIPPED_AS_CLASS_CONST_FETCH_CLASS , \true);
107+ // no constant found on new interface? skip node below ClassConstFetch on this rule
108+ return NodeVisitor::DONT_TRAVERSE_CHILDREN ;
109+ }
110+ }
111+ }
112+ }
113+ // continue to next FullyQualified usage
114+ return null ;
115+ }
80116 if ($ oldToNewClasses !== []) {
81117 $ scope = $ node ->getAttribute (AttributeKey::SCOPE );
82118 return $ this ->classRenamer ->renameNode ($ node , $ oldToNewClasses , $ scope );
0 commit comments