8787use function array_shift ;
8888use function count ;
8989use function in_array ;
90+ use function is_array ;
9091use function is_string ;
9192use function strtolower ;
9293use function substr ;
@@ -1972,6 +1973,12 @@ private function createForExpr(
19721973 if (!$ this ->rememberPossiblyImpureFunctionValues && !$ hasSideEffects ->no ()) {
19731974 return new SpecifiedTypes ([], []);
19741975 }
1976+
1977+ foreach ($ expr ->getArgs () as $ arg ) {
1978+ if ($ this ->containsImpureCall ($ arg ->value , $ scope )) {
1979+ return new SpecifiedTypes ([], []);
1980+ }
1981+ }
19751982 }
19761983
19771984 if (
@@ -1992,6 +1999,24 @@ private function createForExpr(
19921999
19932000 return new SpecifiedTypes ([], []);
19942001 }
2002+
2003+ if ($ this ->containsImpureCall ($ expr ->var , $ scope )) {
2004+ if (isset ($ containsNull ) && !$ containsNull ) {
2005+ return $ this ->createNullsafeTypes ($ originalExpr , $ scope , $ context , $ type );
2006+ }
2007+
2008+ return new SpecifiedTypes ([], []);
2009+ }
2010+
2011+ foreach ($ expr ->getArgs () as $ arg ) {
2012+ if ($ this ->containsImpureCall ($ arg ->value , $ scope )) {
2013+ if (isset ($ containsNull ) && !$ containsNull ) {
2014+ return $ this ->createNullsafeTypes ($ originalExpr , $ scope , $ context , $ type );
2015+ }
2016+
2017+ return new SpecifiedTypes ([], []);
2018+ }
2019+ }
19952020 }
19962021
19972022 if (
@@ -2017,6 +2042,16 @@ private function createForExpr(
20172042
20182043 return new SpecifiedTypes ([], []);
20192044 }
2045+
2046+ foreach ($ expr ->getArgs () as $ arg ) {
2047+ if ($ this ->containsImpureCall ($ arg ->value , $ scope )) {
2048+ if (isset ($ containsNull ) && !$ containsNull ) {
2049+ return $ this ->createNullsafeTypes ($ originalExpr , $ scope , $ context , $ type );
2050+ }
2051+
2052+ return new SpecifiedTypes ([], []);
2053+ }
2054+ }
20202055 }
20212056
20222057 $ sureTypes = [];
@@ -2047,6 +2082,74 @@ private function createForExpr(
20472082 return $ types ;
20482083 }
20492084
2085+ private function containsImpureCall (Expr $ expr , Scope $ scope ): bool
2086+ {
2087+ if ($ expr instanceof FuncCall && $ expr ->name instanceof Name) {
2088+ if (!$ this ->reflectionProvider ->hasFunction ($ expr ->name , $ scope )) {
2089+ return true ;
2090+ }
2091+ $ functionReflection = $ this ->reflectionProvider ->getFunction ($ expr ->name , $ scope );
2092+ $ hasSideEffects = $ functionReflection ->hasSideEffects ();
2093+ if ($ hasSideEffects ->yes ()) {
2094+ return true ;
2095+ }
2096+ if (!$ this ->rememberPossiblyImpureFunctionValues && !$ hasSideEffects ->no ()) {
2097+ return true ;
2098+ }
2099+ }
2100+
2101+ if ($ expr instanceof MethodCall && $ expr ->name instanceof Node \Identifier) {
2102+ $ calledOnType = $ scope ->getType ($ expr ->var );
2103+ $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ expr ->name ->toString ());
2104+ if (
2105+ $ methodReflection === null
2106+ || $ methodReflection ->hasSideEffects ()->yes ()
2107+ || (!$ this ->rememberPossiblyImpureFunctionValues && !$ methodReflection ->hasSideEffects ()->no ())
2108+ ) {
2109+ return true ;
2110+ }
2111+ }
2112+
2113+ if ($ expr instanceof StaticCall && $ expr ->name instanceof Node \Identifier) {
2114+ if ($ expr ->class instanceof Name) {
2115+ $ calledOnType = $ scope ->resolveTypeByName ($ expr ->class );
2116+ } else {
2117+ $ calledOnType = $ scope ->getType ($ expr ->class );
2118+ }
2119+ $ methodReflection = $ scope ->getMethodReflection ($ calledOnType , $ expr ->name ->toString ());
2120+ if (
2121+ $ methodReflection === null
2122+ || $ methodReflection ->hasSideEffects ()->yes ()
2123+ || (!$ this ->rememberPossiblyImpureFunctionValues && !$ methodReflection ->hasSideEffects ()->no ())
2124+ ) {
2125+ return true ;
2126+ }
2127+ }
2128+
2129+ foreach ($ expr ->getSubNodeNames () as $ name ) {
2130+ $ subNode = $ expr ->{$ name };
2131+ if ($ subNode instanceof Expr) {
2132+ if ($ this ->containsImpureCall ($ subNode , $ scope )) {
2133+ return true ;
2134+ }
2135+ }
2136+ if (!is_array ($ subNode )) {
2137+ continue ;
2138+ }
2139+
2140+ foreach ($ subNode as $ item ) {
2141+ if ($ item instanceof Node \Arg && $ this ->containsImpureCall ($ item ->value , $ scope )) {
2142+ return true ;
2143+ }
2144+ if ($ item instanceof Expr && $ this ->containsImpureCall ($ item , $ scope )) {
2145+ return true ;
2146+ }
2147+ }
2148+ }
2149+
2150+ return false ;
2151+ }
2152+
20502153 private function createNullsafeTypes (Expr $ expr , Scope $ scope , TypeSpecifierContext $ context , ?Type $ type ): SpecifiedTypes
20512154 {
20522155 if ($ expr instanceof Expr \NullsafePropertyFetch) {
0 commit comments