148
148
use function is_numeric ;
149
149
use function is_string ;
150
150
use function ltrim ;
151
+ use function md5 ;
151
152
use function sprintf ;
152
153
use function str_starts_with ;
153
154
use function strlen ;
@@ -702,6 +703,30 @@ private function getNodeKey(Expr $node): string
702
703
return $ key ;
703
704
}
704
705
706
+ private function getClosureScopeCacheKey (): string
707
+ {
708
+ $ parts = [];
709
+ foreach ($ this ->expressionTypes as $ exprString => $ expressionTypeHolder ) {
710
+ $ parts [] = sprintf ('%s::%s ' , $ exprString , $ expressionTypeHolder ->getType ()->describe (VerbosityLevel::cache ()));
711
+ }
712
+ $ parts [] = '--- ' ;
713
+ foreach ($ this ->nativeExpressionTypes as $ exprString => $ expressionTypeHolder ) {
714
+ $ parts [] = sprintf ('%s::%s ' , $ exprString , $ expressionTypeHolder ->getType ()->describe (VerbosityLevel::cache ()));
715
+ }
716
+
717
+ $ parts [] = sprintf (':%d ' , count ($ this ->inFunctionCallsStack ));
718
+ foreach ($ this ->inFunctionCallsStack as [$ method , $ parameter ]) {
719
+ if ($ parameter === null ) {
720
+ $ parts [] = ',null ' ;
721
+ continue ;
722
+ }
723
+
724
+ $ parts [] = sprintf (',%s ' , $ parameter ->getType ()->describe (VerbosityLevel::cache ()));
725
+ }
726
+
727
+ return md5 (implode ("\n" , $ parts ));
728
+ }
729
+
705
730
private function resolveType (string $ exprString , Expr $ node ): Type
706
731
{
707
732
foreach ($ this ->expressionTypeResolverExtensionRegistry ->getExtensions () as $ extension ) {
@@ -1318,6 +1343,25 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
1318
1343
$ impurePoints = array_merge ($ arrowFunctionImpurePoints , $ arrowFunctionExprResult ->getImpurePoints ());
1319
1344
$ usedVariables = [];
1320
1345
} else {
1346
+ $ cachedTypes = $ node ->getAttribute ('phpstanCachedTypes ' , []);
1347
+ $ cacheKey = $ this ->getClosureScopeCacheKey ();
1348
+ if (array_key_exists ($ cacheKey , $ cachedTypes )) {
1349
+ $ cachedClosureData = $ cachedTypes [$ cacheKey ];
1350
+
1351
+ return new ClosureType (
1352
+ $ parameters ,
1353
+ $ cachedClosureData ['returnType ' ],
1354
+ $ isVariadic ,
1355
+ TemplateTypeMap::createEmpty (),
1356
+ TemplateTypeMap::createEmpty (),
1357
+ TemplateTypeVarianceMap::createEmpty (),
1358
+ [],
1359
+ $ cachedClosureData ['throwPoints ' ],
1360
+ $ cachedClosureData ['impurePoints ' ],
1361
+ $ cachedClosureData ['invalidateExpressions ' ],
1362
+ $ cachedClosureData ['usedVariables ' ],
1363
+ );
1364
+ }
1321
1365
if (self ::$ resolveClosureTypeDepth >= 2 ) {
1322
1366
return new ClosureType (
1323
1367
$ parameters ,
@@ -1483,6 +1527,19 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
1483
1527
}
1484
1528
}
1485
1529
1530
+ $ throwPointsForClosureType = array_map (static fn (ThrowPoint $ throwPoint ) => $ throwPoint ->isExplicit () ? SimpleThrowPoint::createExplicit ($ throwPoint ->getType (), $ throwPoint ->canContainAnyThrowable ()) : SimpleThrowPoint::createImplicit (), $ throwPoints );
1531
+ $ impurePointsForClosureType = array_map (static fn (ImpurePoint $ impurePoint ) => new SimpleImpurePoint ($ impurePoint ->getIdentifier (), $ impurePoint ->getDescription (), $ impurePoint ->isCertain ()), $ impurePoints );
1532
+
1533
+ $ cachedTypes = $ node ->getAttribute ('phpstanCachedTypes ' , []);
1534
+ $ cachedTypes [$ this ->getClosureScopeCacheKey ()] = [
1535
+ 'returnType ' => $ returnType ,
1536
+ 'throwPoints ' => $ throwPointsForClosureType ,
1537
+ 'impurePoints ' => $ impurePointsForClosureType ,
1538
+ 'invalidateExpressions ' => $ invalidateExpressions ,
1539
+ 'usedVariables ' => $ usedVariables ,
1540
+ ];
1541
+ $ node ->setAttribute ('phpstanCachedTypes ' , $ cachedTypes );
1542
+
1486
1543
return new ClosureType (
1487
1544
$ parameters ,
1488
1545
$ returnType ,
@@ -1491,8 +1548,8 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
1491
1548
TemplateTypeMap::createEmpty (),
1492
1549
TemplateTypeVarianceMap::createEmpty (),
1493
1550
[],
1494
- array_map ( static fn ( ThrowPoint $ throwPoint ) => $ throwPoint -> isExplicit () ? SimpleThrowPoint:: createExplicit ( $ throwPoint -> getType (), $ throwPoint -> canContainAnyThrowable ()) : SimpleThrowPoint:: createImplicit (), $ throwPoints ) ,
1495
- array_map ( static fn ( ImpurePoint $ impurePoint ) => new SimpleImpurePoint ( $ impurePoint -> getIdentifier (), $ impurePoint -> getDescription (), $ impurePoint -> isCertain ()), $ impurePoints ) ,
1551
+ $ throwPointsForClosureType ,
1552
+ $ impurePointsForClosureType ,
1496
1553
$ invalidateExpressions ,
1497
1554
$ usedVariables ,
1498
1555
);
0 commit comments