1515use PhpParser \Node \Identifier ;
1616use PhpParser \Node \Name ;
1717use PhpParser \Node \Stmt \ClassMethod ;
18+ use PHPStan \Reflection \ClassReflection ;
1819use Rector \Arguments \Contract \ReplaceArgumentDefaultValueInterface ;
1920use Rector \Arguments \ValueObject \ReplaceArgumentDefaultValue ;
2021use Rector \NodeAnalyzer \ArgsAnalyzer ;
2122use Rector \NodeTypeResolver \NodeTypeResolver ;
2223use Rector \PhpParser \AstResolver ;
2324use Rector \PhpParser \Node \NodeFactory ;
2425use Rector \PhpParser \Node \Value \ValueResolver ;
26+ use Rector \Reflection \ReflectionResolver ;
2527use Rector \StaticTypeMapper \ValueObject \Type \FullyQualifiedObjectType ;
2628final class ArgumentDefaultValueReplacer
2729{
@@ -45,13 +47,18 @@ final class ArgumentDefaultValueReplacer
4547 * @readonly
4648 */
4749 private NodeTypeResolver $ nodeTypeResolver ;
48- public function __construct (NodeFactory $ nodeFactory , ValueResolver $ valueResolver , ArgsAnalyzer $ argsAnalyzer , AstResolver $ astResolver , NodeTypeResolver $ nodeTypeResolver )
50+ /**
51+ * @readonly
52+ */
53+ private ReflectionResolver $ reflectionResolver ;
54+ public function __construct (NodeFactory $ nodeFactory , ValueResolver $ valueResolver , ArgsAnalyzer $ argsAnalyzer , AstResolver $ astResolver , NodeTypeResolver $ nodeTypeResolver , ReflectionResolver $ reflectionResolver )
4955 {
5056 $ this ->nodeFactory = $ nodeFactory ;
5157 $ this ->valueResolver = $ valueResolver ;
5258 $ this ->argsAnalyzer = $ argsAnalyzer ;
5359 $ this ->astResolver = $ astResolver ;
5460 $ this ->nodeTypeResolver = $ nodeTypeResolver ;
61+ $ this ->reflectionResolver = $ reflectionResolver ;
5562 }
5663 /**
5764 * @template TCall as (MethodCall|StaticCall|ClassMethod|FuncCall|New_)
@@ -156,6 +163,18 @@ private function processArgs($expr, ReplaceArgumentDefaultValueInterface $replac
156163 return null ;
157164 }
158165 }
166+ // when the replacement value is a self::/static::/parent:: constant, it only
167+ // resolves correctly if the class around the call actually has that constant;
168+ // otherwise the produced code would reference a non-existing constant
169+ if ($ normalizedValueAfter instanceof ClassConstFetch && $ normalizedValueAfter ->class instanceof Name && $ normalizedValueAfter ->class ->isSpecialClassName () && $ normalizedValueAfter ->name instanceof Identifier) {
170+ $ classReflection = $ this ->reflectionResolver ->resolveClassReflection ($ expr );
171+ if (!$ classReflection instanceof ClassReflection) {
172+ return null ;
173+ }
174+ if (!$ classReflection ->hasConstant ($ normalizedValueAfter ->name ->toString ())) {
175+ return null ;
176+ }
177+ }
159178 $ particularArg ->value = $ normalizedValueAfter ;
160179 return $ expr ;
161180 }
0 commit comments