@@ -187,7 +187,6 @@ class NodeScopeResolver
187187
188188 private const LOOP_SCOPE_ITERATIONS = 3 ;
189189 private const GENERALIZE_AFTER_ITERATION = 1 ;
190- private const FOREACH_UNROLL_LIMIT = 8 ;
191190
192191 /** @var array<string, true> filePath(string) => bool(true) */
193192 private array $ analysedFiles = [];
@@ -1384,106 +1383,6 @@ public function processStmtNode(
13841383 // get types from finalScope, but don't create new variables
13851384 }
13861385
1387- if (
1388- $ context ->isTopLevel ()
1389- && $ isIterableAtLeastOnce ->yes ()
1390- && count ($ breakExitPoints ) === 0
1391- && !$ stmt ->byRef
1392- && $ exprType ->isConstantArray ()->yes ()
1393- && $ stmt ->valueVar instanceof Variable && is_string ($ stmt ->valueVar ->name )
1394- && ($ stmt ->keyVar === null || ($ stmt ->keyVar instanceof Variable && is_string ($ stmt ->keyVar ->name )))
1395- ) {
1396- $ constantArraysForUnroll = $ exprType ->getConstantArrays ();
1397- if (
1398- count ($ constantArraysForUnroll ) === 1
1399- && count ($ constantArraysForUnroll [0 ]->getOptionalKeys ()) === 0
1400- && ($ unrollKeyCount = count ($ constantArraysForUnroll [0 ]->getKeyTypes ())) > 0
1401- && $ unrollKeyCount <= self ::FOREACH_UNROLL_LIMIT
1402- ) {
1403- $ unrolledScope = $ scope ;
1404- $ unrollSucceeded = true ;
1405- foreach ($ constantArraysForUnroll [0 ]->getKeyTypes () as $ i => $ keyType ) {
1406- $ valueType = $ constantArraysForUnroll [0 ]->getValueTypes ()[$ i ];
1407-
1408- $ iterScope = $ unrolledScope ->assignVariable (
1409- $ stmt ->valueVar ->name ,
1410- $ valueType ,
1411- $ valueType ,
1412- TrinaryLogic::createYes (),
1413- );
1414- if ($ stmt ->keyVar instanceof Variable && is_string ($ stmt ->keyVar ->name )) {
1415- $ iterScope = $ iterScope ->assignVariable (
1416- $ stmt ->keyVar ->name ,
1417- $ keyType ,
1418- $ keyType ,
1419- TrinaryLogic::createYes (),
1420- );
1421- }
1422-
1423- $ iterStorage = $ storage ->duplicate ();
1424- $ iterResult = $ this ->processStmtNodesInternal (
1425- $ stmt , $ stmt ->stmts , $ iterScope , $ iterStorage ,
1426- new NoopNodeCallback (), $ context ->enterDeep (),
1427- )->filterOutLoopExitPoints ();
1428-
1429- $ unrolledScope = $ iterResult ->getScope ();
1430- foreach ($ iterResult ->getExitPointsByType (Continue_::class) as $ continueExitPoint ) {
1431- $ unrolledScope = $ unrolledScope ->mergeWith ($ continueExitPoint ->getScope ());
1432- }
1433-
1434- if (
1435- count ($ iterResult ->getExitPointsByType (Break_::class)) > 0
1436- || $ iterResult ->isAlwaysTerminating ()
1437- ) {
1438- $ unrollSucceeded = false ;
1439- break ;
1440- }
1441- }
1442-
1443- if ($ unrollSucceeded ) {
1444- foreach ($ unrolledScope ->expressionTypes as $ exprString => $ holder ) {
1445- if (!str_starts_with ($ exprString , '$ ' ) || str_contains ($ exprString , '[ ' ) || str_contains ($ exprString , '> ' )) {
1446- continue ;
1447- }
1448- if (!$ holder ->getCertainty ()->yes ()) {
1449- continue ;
1450- }
1451- $ unrolledType = $ holder ->getType ();
1452- if (!$ unrolledType ->isConstantArray ()->yes ()) {
1453- continue ;
1454- }
1455- if (!isset ($ finalScope ->expressionTypes [$ exprString ])) {
1456- continue ;
1457- }
1458- $ finalHolder = $ finalScope ->expressionTypes [$ exprString ];
1459- $ finalType = $ finalHolder ->getType ();
1460- if (!$ finalType ->isConstantArray ()->yes ()) {
1461- continue ;
1462- }
1463-
1464- $ unrolledArrays = $ unrolledType ->getConstantArrays ();
1465- $ finalArrays = $ finalType ->getConstantArrays ();
1466- if (
1467- count ($ unrolledArrays ) === 1
1468- && count ($ finalArrays ) === 1
1469- && count ($ unrolledArrays [0 ]->getOptionalKeys ()) < count ($ finalArrays [0 ]->getOptionalKeys ())
1470- && count ($ unrolledArrays [0 ]->getKeyTypes ()) === count ($ finalArrays [0 ]->getKeyTypes ())
1471- ) {
1472- $ varName = substr ($ exprString , 1 );
1473- $ varExpr = $ holder ->getExpr ();
1474- $ nativeType = $ unrolledScope ->getNativeType ($ varExpr );
1475- $ finalScope = $ finalScope ->assignVariable (
1476- $ varName ,
1477- $ unrolledType ,
1478- $ nativeType ,
1479- TrinaryLogic::createYes (),
1480- );
1481- }
1482- }
1483- }
1484- }
1485- }
1486-
14871386 if (!$ isIterableAtLeastOnce ->no ()) {
14881387 $ throwPoints = array_merge ($ throwPoints , $ finalScopeResult ->getThrowPoints ());
14891388 $ impurePoints = array_merge ($ impurePoints , $ finalScopeResult ->getImpurePoints ());
0 commit comments