From ef44511d34f035835606cbb02347c4a6a5180d5d Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 17 Jul 2026 11:57:08 +0200 Subject: [PATCH] Iterate through nullsafe expression chains Replace recursive calls in NullsafeShortCircuitingHelper::getType() with a loop while preserving the same expression-chain cases and return behavior. This helper is a flat CPU hotspot under expression type resolution. Iteration avoids repeated PHP method calls and stack frames for chained property, method, array, and static accesses. Benchmark: uncached Sylius analysis of 2,366 files with eight workers, five runs per variant. Median elapsed time fell from 28.35 s to 27.22 s (-4.0%) and aggregate user+sys CPU from 172.90 s to 170.65 s (-1.3%). In the combined profile, helper CPU fell from 5.72 s to 2.70 s (-52.8%). --- .../Helper/NullsafeShortCircuitingHelper.php | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/Analyser/ExprHandler/Helper/NullsafeShortCircuitingHelper.php b/src/Analyser/ExprHandler/Helper/NullsafeShortCircuitingHelper.php index 72710dd93b5..9c0c48d94d2 100644 --- a/src/Analyser/ExprHandler/Helper/NullsafeShortCircuitingHelper.php +++ b/src/Analyser/ExprHandler/Helper/NullsafeShortCircuitingHelper.php @@ -19,36 +19,28 @@ final class NullsafeShortCircuitingHelper public static function getType(MutatingScope $scope, Expr $expr, Type $type): Type { - if ($expr instanceof NullsafePropertyFetch || $expr instanceof NullsafeMethodCall) { - $varType = $scope->getType($expr->var); - if (TypeCombinator::containsNull($varType)) { - return TypeCombinator::addNull($type); + while (true) { + if ($expr instanceof NullsafePropertyFetch || $expr instanceof NullsafeMethodCall) { + $varType = $scope->getType($expr->var); + if (TypeCombinator::containsNull($varType)) { + return TypeCombinator::addNull($type); + } + + return $type; } - return $type; - } - - if ($expr instanceof ArrayDimFetch) { - return self::getType($scope, $expr->var, $type); - } - - if ($expr instanceof PropertyFetch) { - return self::getType($scope, $expr->var, $type); - } - - if ($expr instanceof StaticPropertyFetch && $expr->class instanceof Expr) { - return self::getType($scope, $expr->class, $type); - } + if ($expr instanceof ArrayDimFetch || $expr instanceof PropertyFetch || $expr instanceof MethodCall) { + $expr = $expr->var; + continue; + } - if ($expr instanceof MethodCall) { - return self::getType($scope, $expr->var, $type); - } + if (($expr instanceof StaticPropertyFetch || $expr instanceof StaticCall) && $expr->class instanceof Expr) { + $expr = $expr->class; + continue; + } - if ($expr instanceof StaticCall && $expr->class instanceof Expr) { - return self::getType($scope, $expr->class, $type); + return $type; } - - return $type; } }