44
55use Nette \Utils \Strings ;
66use PhpParser \Node ;
7+ use PhpParser \Node \Arg ;
78use PhpParser \Node \Expr ;
89use PhpParser \Node \Expr \Array_ ;
910use PhpParser \Node \Expr \BinaryOp ;
1920use PhpParser \Node \Expr \PropertyFetch ;
2021use PhpParser \Node \Expr \Variable ;
2122use PhpParser \Node \Name ;
23+ use PhpParser \Node \Name \FullyQualified ;
2224use PhpParser \Node \Scalar \DNumber ;
2325use PhpParser \Node \Scalar \EncapsedStringPart ;
2426use PhpParser \Node \Scalar \LNumber ;
2729use PHPStan \Reflection \ClassReflection ;
2830use PHPStan \Reflection \ConstantReflection ;
2931use PHPStan \Reflection \Dummy \DummyConstructorReflection ;
32+ use PHPStan \Reflection \FunctionReflection ;
3033use PHPStan \Reflection \MethodReflection ;
3134use PHPStan \Reflection \Native \NativeParameterReflection ;
3235use PHPStan \Reflection \ParametersAcceptor ;
@@ -149,6 +152,9 @@ class MutatingScope implements Scope
149152 /** @var array<string, true> */
150153 private $ currentlyAssignedExpressions = [];
151154
155+ /** @var array<MethodReflection|FunctionReflection> */
156+ private $ inFunctionCallsStack = [];
157+
152158 /** @var array<string, Type> */
153159 private $ nativeExpressionTypes ;
154160
@@ -177,6 +183,7 @@ class MutatingScope implements Scope
177183 * @param bool $inFirstLevelStatement
178184 * @param array<string, true> $currentlyAssignedExpressions
179185 * @param array<string, Type> $nativeExpressionTypes
186+ * @param array<MethodReflection|FunctionReflection> $inFunctionCallsStack
180187 * @param string[] $dynamicConstantNames
181188 * @paarm bool $treatPhpDocTypesAsCertain
182189 */
@@ -199,6 +206,7 @@ public function __construct(
199206 bool $ inFirstLevelStatement = true ,
200207 array $ currentlyAssignedExpressions = [],
201208 array $ nativeExpressionTypes = [],
209+ array $ inFunctionCallsStack = [],
202210 array $ dynamicConstantNames = [],
203211 bool $ treatPhpDocTypesAsCertain = true
204212 )
@@ -225,6 +233,7 @@ public function __construct(
225233 $ this ->inFirstLevelStatement = $ inFirstLevelStatement ;
226234 $ this ->currentlyAssignedExpressions = $ currentlyAssignedExpressions ;
227235 $ this ->nativeExpressionTypes = $ nativeExpressionTypes ;
236+ $ this ->inFunctionCallsStack = $ inFunctionCallsStack ;
228237 $ this ->dynamicConstantNames = $ dynamicConstantNames ;
229238 $ this ->treatPhpDocTypesAsCertain = $ treatPhpDocTypesAsCertain ;
230239 }
@@ -1793,6 +1802,7 @@ public function doNotTreatPhpDocTypesAsCertain(): Scope
17931802 $ this ->inFirstLevelStatement ,
17941803 $ this ->currentlyAssignedExpressions ,
17951804 $ this ->nativeExpressionTypes ,
1805+ $ this ->inFunctionCallsStack ,
17961806 $ this ->dynamicConstantNames ,
17971807 false
17981808 );
@@ -2027,6 +2037,74 @@ public function isSpecified(Expr $node): bool
20272037 && $ this ->moreSpecificTypes [$ exprString ]->getCertainty ()->yes ();
20282038 }
20292039
2040+ /**
2041+ * @param MethodReflection|FunctionReflection $reflection
2042+ * @return self
2043+ */
2044+ public function pushInFunctionCall ($ reflection ): self
2045+ {
2046+ $ stack = $ this ->inFunctionCallsStack ;
2047+ $ stack [] = $ reflection ;
2048+
2049+ return $ this ->scopeFactory ->create (
2050+ $ this ->context ,
2051+ $ this ->isDeclareStrictTypes (),
2052+ $ this ->getFunction (),
2053+ $ this ->getNamespace (),
2054+ $ this ->getVariableTypes (),
2055+ $ this ->moreSpecificTypes ,
2056+ $ this ->inClosureBindScopeClass ,
2057+ $ this ->anonymousFunctionReflection ,
2058+ $ this ->isInFirstLevelStatement (),
2059+ $ this ->currentlyAssignedExpressions ,
2060+ $ this ->nativeExpressionTypes ,
2061+ $ stack
2062+ );
2063+ }
2064+
2065+ public function popInFunctionCall (): self
2066+ {
2067+ $ stack = $ this ->inFunctionCallsStack ;
2068+ array_pop ($ stack );
2069+
2070+ return $ this ->scopeFactory ->create (
2071+ $ this ->context ,
2072+ $ this ->isDeclareStrictTypes (),
2073+ $ this ->getFunction (),
2074+ $ this ->getNamespace (),
2075+ $ this ->getVariableTypes (),
2076+ $ this ->moreSpecificTypes ,
2077+ $ this ->inClosureBindScopeClass ,
2078+ $ this ->anonymousFunctionReflection ,
2079+ $ this ->isInFirstLevelStatement (),
2080+ $ this ->currentlyAssignedExpressions ,
2081+ $ this ->nativeExpressionTypes ,
2082+ $ stack
2083+ );
2084+ }
2085+
2086+ public function isInClassExists (string $ className ): bool
2087+ {
2088+ foreach ($ this ->inFunctionCallsStack as $ inFunctionCall ) {
2089+ if (!$ inFunctionCall instanceof FunctionReflection) {
2090+ continue ;
2091+ }
2092+
2093+ if (in_array ($ inFunctionCall ->getName (), [
2094+ 'class_exists ' ,
2095+ 'interface_exists ' ,
2096+ 'trait_exists ' ,
2097+ ], true )) {
2098+ return true ;
2099+ }
2100+ }
2101+ $ expr = new FuncCall (new FullyQualified ('class_exists ' ), [
2102+ new Arg (new String_ ($ className )),
2103+ ]);
2104+
2105+ return (new ConstantBooleanType (true ))->isSuperTypeOf ($ this ->getType ($ expr ))->yes ();
2106+ }
2107+
20302108 public function enterClass (ClassReflection $ classReflection ): self
20312109 {
20322110 return $ this ->scopeFactory ->create (
@@ -2628,7 +2706,8 @@ public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $
26282706 $ this ->anonymousFunctionReflection ,
26292707 $ this ->inFirstLevelStatement ,
26302708 $ this ->currentlyAssignedExpressions ,
2631- $ nativeTypes
2709+ $ nativeTypes ,
2710+ $ this ->inFunctionCallsStack
26322711 );
26332712 }
26342713
@@ -2710,7 +2789,8 @@ public function specifyExpressionType(Expr $expr, Type $type): self
27102789 $ this ->anonymousFunctionReflection ,
27112790 $ this ->inFirstLevelStatement ,
27122791 $ this ->currentlyAssignedExpressions ,
2713- $ nativeTypes
2792+ $ nativeTypes ,
2793+ $ this ->inFunctionCallsStack
27142794 );
27152795 } elseif ($ expr instanceof Expr \ArrayDimFetch && $ expr ->dim !== null ) {
27162796 $ constantArrays = TypeUtils::getConstantArrays ($ this ->getType ($ expr ->var ));
@@ -2881,7 +2961,8 @@ public function exitFirstLevelStatements(): self
28812961 $ this ->anonymousFunctionReflection ,
28822962 false ,
28832963 $ this ->currentlyAssignedExpressions ,
2884- $ this ->nativeExpressionTypes
2964+ $ this ->nativeExpressionTypes ,
2965+ $ this ->inFunctionCallsStack
28852966 );
28862967 }
28872968
@@ -2946,7 +3027,8 @@ public function mergeWith(?self $otherScope): self
29463027 array_map ($ typeToVariableHolder , $ otherScope ->nativeExpressionTypes )
29473028 ), static function (VariableTypeHolder $ holder ): bool {
29483029 return $ holder ->getCertainty ()->yes ();
2949- }))
3030+ })),
3031+ []
29503032 );
29513033 }
29523034
@@ -3009,7 +3091,8 @@ public function processFinallyScope(self $finallyScope, self $originalFinallySco
30093091 array_map ($ typeToVariableHolder , $ this ->nativeExpressionTypes ),
30103092 array_map ($ typeToVariableHolder , $ finallyScope ->nativeExpressionTypes ),
30113093 array_map ($ typeToVariableHolder , $ originalFinallyScope ->nativeExpressionTypes )
3012- ))
3094+ )),
3095+ []
30133096 );
30143097 }
30153098
@@ -3097,7 +3180,8 @@ public function processClosureScope(
30973180 $ this ->anonymousFunctionReflection ,
30983181 $ this ->inFirstLevelStatement ,
30993182 [],
3100- $ this ->nativeExpressionTypes
3183+ $ this ->nativeExpressionTypes ,
3184+ $ this ->inFunctionCallsStack
31013185 );
31023186 }
31033187
@@ -3142,7 +3226,8 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
31423226 $ this ->anonymousFunctionReflection ,
31433227 $ this ->inFirstLevelStatement ,
31443228 [],
3145- $ nativeTypes
3229+ $ nativeTypes ,
3230+ []
31463231 );
31473232 }
31483233
@@ -3180,7 +3265,8 @@ public function generalizeWith(self $otherScope): self
31803265 $ this ->anonymousFunctionReflection ,
31813266 $ this ->inFirstLevelStatement ,
31823267 [],
3183- $ nativeTypes
3268+ $ nativeTypes ,
3269+ []
31843270 );
31853271 }
31863272
0 commit comments