@@ -3900,113 +3900,141 @@ private function tryProcessUnrolledConstantArrayForeach(
39003900 return null ;
39013901 }
39023902 $ constantArrays = $ iterateeType ->getConstantArrays ();
3903- if (count ($ constantArrays ) !== 1 ) {
3903+ if (count ($ constantArrays ) === 0 ) {
39043904 return null ;
39053905 }
3906- $ constantArray = $ constantArrays [0 ];
3907- $ keyTypes = $ constantArray ->getKeyTypes ();
3908- $ valueTypes = $ constantArray ->getValueTypes ();
3909- if (count ($ keyTypes ) === 0 || count ($ keyTypes ) > self ::FOREACH_UNROLL_LIMIT ) {
3906+
3907+ $ totalKeys = 0 ;
3908+ foreach ($ constantArrays as $ constantArray ) {
3909+ $ totalKeys += count ($ constantArray ->getKeyTypes ());
3910+ }
3911+ if ($ totalKeys === 0 || $ totalKeys > self ::FOREACH_UNROLL_LIMIT ) {
39103912 return null ;
39113913 }
39123914
39133915 $ nativeIterateeType = $ originalScope ->getNativeType ($ stmt ->expr );
39143916 $ nativeConstantArrays = $ nativeIterateeType ->getConstantArrays ();
3915- $ nativeConstantArray = count ($ nativeConstantArrays ) === 1 ? $ nativeConstantArrays[ 0 ] : null ;
3917+ $ matchedNativeArrays = count ($ nativeConstantArrays ) === count ( $ constantArrays ) ? $ nativeConstantArrays : null ;
39163918
3917- $ optionalKeys = array_fill_keys ($ constantArray ->getOptionalKeys (), true );
39183919 $ valueVarName = $ stmt ->valueVar ->name ;
39193920 $ keyVarName = $ stmt ->keyVar instanceof Variable && is_string ($ stmt ->keyVar ->name ) ? $ stmt ->keyVar ->name : null ;
39203921
3921- $ chainScope = $ originalScope ;
3922- $ entryScopes = [];
3923- $ breakScopes = [];
3924- foreach ($ keyTypes as $ i => $ keyType ) {
3925- $ valueType = $ valueTypes [$ i ];
3926- $ isOptional = isset ($ optionalKeys [$ i ]);
3927-
3928- $ nativeKeyType = $ nativeConstantArray !== null && isset ($ nativeConstantArray ->getKeyTypes ()[$ i ])
3929- ? $ nativeConstantArray ->getKeyTypes ()[$ i ]
3930- : $ keyType ;
3931- $ nativeValueType = $ nativeConstantArray !== null && isset ($ nativeConstantArray ->getValueTypes ()[$ i ])
3932- ? $ nativeConstantArray ->getValueTypes ()[$ i ]
3933- : $ valueType ;
3934-
3935- $ iterScope = $ chainScope ->assignVariable (
3936- $ valueVarName ,
3937- $ valueType ,
3938- $ nativeValueType ,
3939- TrinaryLogic::createYes (),
3940- );
3941- $ iterScope = $ iterScope ->assignExpression (
3942- new OriginalForeachValueExpr ($ valueVarName ),
3943- $ valueType ,
3944- $ nativeValueType ,
3945- );
3946- if ($ keyVarName !== null ) {
3947- $ iterScope = $ iterScope ->assignVariable (
3948- $ keyVarName ,
3949- $ keyType ,
3950- $ nativeKeyType ,
3922+ $ allBodyScopes = [];
3923+ $ allChainScopes = [];
3924+ $ allBreakScopes = [];
3925+
3926+ foreach ($ constantArrays as $ arrayIndex => $ constantArray ) {
3927+ $ keyTypes = $ constantArray ->getKeyTypes ();
3928+ $ valueTypes = $ constantArray ->getValueTypes ();
3929+ if (count ($ keyTypes ) === 0 ) {
3930+ continue ;
3931+ }
3932+
3933+ $ nativeConstantArray = $ matchedNativeArrays !== null ? $ matchedNativeArrays [$ arrayIndex ] : null ;
3934+ $ optionalKeys = array_fill_keys ($ constantArray ->getOptionalKeys (), true );
3935+
3936+ $ chainScope = $ originalScope ;
3937+ $ entryScopes = [];
3938+
3939+ foreach ($ keyTypes as $ i => $ keyType ) {
3940+ $ valueType = $ valueTypes [$ i ];
3941+ $ isOptional = isset ($ optionalKeys [$ i ]);
3942+
3943+ $ nativeKeyType = $ nativeConstantArray !== null && isset ($ nativeConstantArray ->getKeyTypes ()[$ i ])
3944+ ? $ nativeConstantArray ->getKeyTypes ()[$ i ]
3945+ : $ keyType ;
3946+ $ nativeValueType = $ nativeConstantArray !== null && isset ($ nativeConstantArray ->getValueTypes ()[$ i ])
3947+ ? $ nativeConstantArray ->getValueTypes ()[$ i ]
3948+ : $ valueType ;
3949+
3950+ $ iterScope = $ chainScope ->assignVariable (
3951+ $ valueVarName ,
3952+ $ valueType ,
3953+ $ nativeValueType ,
39513954 TrinaryLogic::createYes (),
39523955 );
39533956 $ iterScope = $ iterScope ->assignExpression (
3954- new OriginalForeachKeyExpr ($ keyVarName ),
3955- $ keyType ,
3956- $ nativeKeyType ,
3957- );
3958- $ iterScope = $ iterScope ->assignExpression (
3959- new ArrayDimFetch ($ stmt ->expr , $ stmt ->keyVar ),
3957+ new OriginalForeachValueExpr ($ valueVarName ),
39603958 $ valueType ,
39613959 $ nativeValueType ,
39623960 );
3963- }
3961+ if ($ keyVarName !== null ) {
3962+ $ iterScope = $ iterScope ->assignVariable (
3963+ $ keyVarName ,
3964+ $ keyType ,
3965+ $ nativeKeyType ,
3966+ TrinaryLogic::createYes (),
3967+ );
3968+ $ iterScope = $ iterScope ->assignExpression (
3969+ new OriginalForeachKeyExpr ($ keyVarName ),
3970+ $ keyType ,
3971+ $ nativeKeyType ,
3972+ );
3973+ $ iterScope = $ iterScope ->assignExpression (
3974+ new ArrayDimFetch ($ stmt ->expr , $ stmt ->keyVar ),
3975+ $ valueType ,
3976+ $ nativeValueType ,
3977+ );
3978+ }
39643979
3965- $ entryScopes [] = $ iterScope ;
3980+ $ entryScopes [] = $ iterScope ;
39663981
3967- $ iterStorage = $ originalStorage ->duplicate ();
3968- $ bodyResult = $ this ->processStmtNodesInternal (
3969- $ stmt ,
3970- $ stmt ->stmts ,
3971- $ iterScope ,
3972- $ iterStorage ,
3973- new NoopNodeCallback (),
3974- $ context ->enterDeep (),
3975- )->filterOutLoopExitPoints ();
3982+ $ iterStorage = $ originalStorage ->duplicate ();
3983+ $ bodyResult = $ this ->processStmtNodesInternal (
3984+ $ stmt ,
3985+ $ stmt ->stmts ,
3986+ $ iterScope ,
3987+ $ iterStorage ,
3988+ new NoopNodeCallback (),
3989+ $ context ->enterDeep (),
3990+ )->filterOutLoopExitPoints ();
39763991
3977- $ iterEndScope = $ bodyResult ->getScope ();
3978- foreach ($ bodyResult ->getExitPointsByType (Continue_::class) as $ continueExitPoint ) {
3979- $ iterEndScope = $ iterEndScope ->mergeWith ($ continueExitPoint ->getScope ());
3980- }
3981- foreach ($ bodyResult ->getExitPointsByType (Break_::class) as $ breakExitPoint ) {
3982- $ breakScopes [] = $ breakExitPoint ->getScope ();
3992+ $ iterEndScope = $ bodyResult ->getScope ();
3993+ foreach ($ bodyResult ->getExitPointsByType (Continue_::class) as $ continueExitPoint ) {
3994+ $ iterEndScope = $ iterEndScope ->mergeWith ($ continueExitPoint ->getScope ());
3995+ }
3996+ foreach ($ bodyResult ->getExitPointsByType (Break_::class) as $ breakExitPoint ) {
3997+ $ allBreakScopes [] = $ breakExitPoint ->getScope ();
3998+ }
3999+
4000+ if ($ isOptional ) {
4001+ $ chainScope = $ iterEndScope ->mergeWith ($ chainScope );
4002+ } else {
4003+ $ chainScope = $ iterEndScope ;
4004+ }
39834005 }
39844006
3985- if ($ isOptional ) {
3986- $ chainScope = $ iterEndScope ->mergeWith ($ chainScope );
3987- } else {
3988- $ chainScope = $ iterEndScope ;
4007+ $ arrayBodyScope = $ entryScopes [0 ];
4008+ for ($ i = 1 , $ c = count ($ entryScopes ); $ i < $ c ; $ i ++) {
4009+ $ arrayBodyScope = $ arrayBodyScope ->mergeWith ($ entryScopes [$ i ]);
39894010 }
4011+ if (count ($ entryScopes ) === 1 ) {
4012+ $ arrayBodyScope = $ arrayBodyScope ->mergeWith ($ chainScope );
4013+ }
4014+
4015+ $ allBodyScopes [] = $ arrayBodyScope ;
4016+ $ allChainScopes [] = $ chainScope ;
39904017 }
39914018
3992- $ bodyScope = $ entryScopes [0 ];
3993- for ($ i = 1 , $ c = count ($ entryScopes ); $ i < $ c ; $ i ++) {
3994- $ bodyScope = $ bodyScope ->mergeWith ($ entryScopes [$ i ]);
4019+ if ($ allBodyScopes === []) {
4020+ return null ;
4021+ }
4022+
4023+ $ bodyScope = $ allBodyScopes [0 ];
4024+ for ($ i = 1 , $ c = count ($ allBodyScopes ); $ i < $ c ; $ i ++) {
4025+ $ bodyScope = $ bodyScope ->mergeWith ($ allBodyScopes [$ i ]);
39954026 }
3996- if (count ($ entryScopes ) === 1 ) {
3997- // For a single-iteration unrolling, the merged entry scope does
3998- // not include any post-body state. Merge the chain end scope in
3999- // so that rules analysing the body see that prior iterations
4000- // (which in this case means: this same iteration, from a rule
4001- // author's perspective) could have modified variables.
4002- $ bodyScope = $ bodyScope ->mergeWith ($ chainScope );
4027+
4028+ $ endScope = $ allChainScopes [0 ];
4029+ for ($ i = 1 , $ c = count ($ allChainScopes ); $ i < $ c ; $ i ++) {
4030+ $ endScope = $ endScope ->mergeWith ($ allChainScopes [$ i ]);
40034031 }
40044032
4005- foreach ($ breakScopes as $ breakScope ) {
4006- $ chainScope = $ chainScope ->mergeWith ($ breakScope );
4033+ foreach ($ allBreakScopes as $ breakScope ) {
4034+ $ endScope = $ endScope ->mergeWith ($ breakScope );
40074035 }
40084036
4009- return ['bodyScope ' => $ bodyScope , 'endScope ' => $ chainScope ];
4037+ return ['bodyScope ' => $ bodyScope , 'endScope ' => $ endScope ];
40104038 }
40114039
40124040 /**
0 commit comments