From b717d5d6f75d665328b482f859b3d58164c6a83f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 9 Feb 2026 07:44:21 +0100 Subject: [PATCH 1/3] Refactor InitializerExprTypeResolver --- .../InitializerExprTypeResolver.php | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 0c5ede18b5..40b3bae07c 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -976,16 +976,8 @@ public function getBitwiseAndTypeFromTypes(Type $leftType, Type $rightType): Typ return $this->getNeverType($leftType, $rightType); } - if ($leftType instanceof IntegerRangeType) { - $leftTypes = $leftType->getFiniteTypes(); - } else { - $leftTypes = $leftType->getConstantScalarTypes(); - } - if ($rightType instanceof IntegerRangeType) { - $rightTypes = $rightType->getFiniteTypes(); - } else { - $rightTypes = $rightType->getConstantScalarTypes(); - } + $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); + $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); $leftTypesCount = count($leftTypes); $rightTypesCount = count($rightTypes); @@ -1071,16 +1063,8 @@ public function getBitwiseOrTypeFromTypes(Type $leftType, Type $rightType): Type return $this->getNeverType($leftType, $rightType); } - if ($leftType instanceof IntegerRangeType) { - $leftTypes = $leftType->getFiniteTypes(); - } else { - $leftTypes = $leftType->getConstantScalarTypes(); - } - if ($rightType instanceof IntegerRangeType) { - $rightTypes = $rightType->getFiniteTypes(); - } else { - $rightTypes = $rightType->getConstantScalarTypes(); - } + $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); + $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); $leftTypesCount = count($leftTypes); $rightTypesCount = count($rightTypes); @@ -1150,22 +1134,26 @@ public function getBitwiseXorType(Expr $left, Expr $right, callable $getTypeCall return $this->getBitwiseXorTypeFromTypes($leftType, $rightType); } + /** + * @return array + */ + private function getFiniteOrConstantScalarTypes(Type $type): array + { + if ($type instanceof IntegerRangeType) { + return $type->getFiniteTypes(); + } + + return $type->getConstantScalarTypes(); + } + public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type { if ($leftType instanceof NeverType || $rightType instanceof NeverType) { return $this->getNeverType($leftType, $rightType); } - if ($leftType instanceof IntegerRangeType) { - $leftTypes = $leftType->getFiniteTypes(); - } else { - $leftTypes = $leftType->getConstantScalarTypes(); - } - if ($rightType instanceof IntegerRangeType) { - $rightTypes = $rightType->getFiniteTypes(); - } else { - $rightTypes = $rightType->getConstantScalarTypes(); - } + $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); + $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); $leftTypesCount = count($leftTypes); $rightTypesCount = count($rightTypes); From 358db2be9154b2375d31c4b4c8b1a8483218e5d8 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 9 Feb 2026 10:24:46 +0100 Subject: [PATCH 2/3] more aggressive refactor --- phpstan-baseline.neon | 4 +- .../InitializerExprTypeResolver.php | 126 ++++++------------ 2 files changed, 45 insertions(+), 85 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 02b0bb3538..23b4baa7a4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -456,7 +456,7 @@ parameters: - rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.' identifier: phpstanApi.instanceofType - count: 22 + count: 18 path: src/Reflection/InitializerExprTypeResolver.php - @@ -474,7 +474,7 @@ parameters: - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.' identifier: phpstanApi.instanceofType - count: 10 + count: 6 path: src/Reflection/InitializerExprTypeResolver.php - diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 40b3bae07c..5cb3c71a51 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -976,39 +976,10 @@ public function getBitwiseAndTypeFromTypes(Type $leftType, Type $rightType): Typ return $this->getNeverType($leftType, $rightType); } - $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); - $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); - - $leftTypesCount = count($leftTypes); - $rightTypesCount = count($rightTypes); - if ($leftTypesCount > 0 && $rightTypesCount > 0) { - $resultTypes = []; - $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; - if (!$generalize) { - foreach ($leftTypes as $leftTypeInner) { - foreach ($rightTypes as $rightTypeInner) { - if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) { - $resultType = $this->getTypeFromValue($leftTypeInner->getValue() & $rightTypeInner->getValue()); - } else { - $leftNumberType = $leftTypeInner->toNumber(); - $rightNumberType = $rightTypeInner->toNumber(); - - if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) { - return new ErrorType(); - } - - if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) { - throw new ShouldNotHappenException(); - } - - $resultType = $this->getTypeFromValue($leftNumberType->getValue() & $rightNumberType->getValue()); - } - $resultTypes[] = $resultType; - } - } - return TypeCombinator::union(...$resultTypes); - } - + $result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a & $b); + if ($result instanceof Type) { + return $result; + } elseif ($result === self::IS_SCALAR_TYPE) { $leftType = $this->optimizeScalarType($leftType); $rightType = $this->optimizeScalarType($rightType); } @@ -1063,39 +1034,10 @@ public function getBitwiseOrTypeFromTypes(Type $leftType, Type $rightType): Type return $this->getNeverType($leftType, $rightType); } - $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); - $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); - - $leftTypesCount = count($leftTypes); - $rightTypesCount = count($rightTypes); - if ($leftTypesCount > 0 && $rightTypesCount > 0) { - $resultTypes = []; - $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; - if (!$generalize) { - foreach ($leftTypes as $leftTypeInner) { - foreach ($rightTypes as $rightTypeInner) { - if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) { - $resultType = $this->getTypeFromValue($leftTypeInner->getValue() | $rightTypeInner->getValue()); - } else { - $leftNumberType = $leftTypeInner->toNumber(); - $rightNumberType = $rightTypeInner->toNumber(); - - if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) { - return new ErrorType(); - } - - if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) { - throw new ShouldNotHappenException(); - } - - $resultType = $this->getTypeFromValue($leftNumberType->getValue() | $rightNumberType->getValue()); - } - $resultTypes[] = $resultType; - } - } - return TypeCombinator::union(...$resultTypes); - } - + $result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a | $b); + if ($result instanceof Type) { + return $result; + } elseif ($result === self::IS_SCALAR_TYPE) { $leftType = $this->optimizeScalarType($leftType); $rightType = $this->optimizeScalarType($rightType); } @@ -1134,27 +1076,27 @@ public function getBitwiseXorType(Expr $left, Expr $right, callable $getTypeCall return $this->getBitwiseXorTypeFromTypes($leftType, $rightType); } + private const IS_SCALAR_TYPE = 1; + private const IS_UNKNOWN = 2; + /** - * @return array + * @param callable(bool|float|int|string|null, bool|float|int|string|null):string $operationCallable + * + * @return self::IS_UNKNOWN|self::IS_SCALAR_TYPE|Type */ - private function getFiniteOrConstantScalarTypes(Type $type): array + private function getFiniteOrConstantScalarTypes(Type $leftType, Type $rightType, callable $operationCallable): int|Type { - if ($type instanceof IntegerRangeType) { - return $type->getFiniteTypes(); + if ($leftType instanceof IntegerRangeType) { + $leftTypes = $leftType->getFiniteTypes(); + } else { + $leftTypes = $leftType->getConstantScalarTypes(); } - - return $type->getConstantScalarTypes(); - } - - public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type - { - if ($leftType instanceof NeverType || $rightType instanceof NeverType) { - return $this->getNeverType($leftType, $rightType); + if ($rightType instanceof IntegerRangeType) { + $rightTypes = $rightType->getFiniteTypes(); + } else { + $rightTypes = $rightType->getConstantScalarTypes(); } - $leftTypes = $this->getFiniteOrConstantScalarTypes($leftType); - $rightTypes = $this->getFiniteOrConstantScalarTypes($rightType); - $leftTypesCount = count($leftTypes); $rightTypesCount = count($rightTypes); if ($leftTypesCount > 0 && $rightTypesCount > 0) { @@ -1164,7 +1106,8 @@ public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Typ foreach ($leftTypes as $leftTypeInner) { foreach ($rightTypes as $rightTypeInner) { if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) { - $resultType = $this->getTypeFromValue($leftTypeInner->getValue() ^ $rightTypeInner->getValue()); + $resultValue = $operationCallable($leftTypeInner->getValue(), $rightTypeInner->getValue()); + $resultType = $this->getTypeFromValue($resultValue); } else { $leftNumberType = $leftTypeInner->toNumber(); $rightNumberType = $rightTypeInner->toNumber(); @@ -1177,7 +1120,8 @@ public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Typ throw new ShouldNotHappenException(); } - $resultType = $this->getTypeFromValue($leftNumberType->getValue() ^ $rightNumberType->getValue()); + $resultValue = $operationCallable($leftNumberType->getValue(), $rightNumberType->getValue()); + $resultType = $this->getTypeFromValue($resultValue); } $resultTypes[] = $resultType; } @@ -1185,6 +1129,22 @@ public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Typ return TypeCombinator::union(...$resultTypes); } + return self::IS_SCALAR_TYPE; + } + + return self::IS_UNKNOWN; + } + + public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type + { + if ($leftType instanceof NeverType || $rightType instanceof NeverType) { + return $this->getNeverType($leftType, $rightType); + } + + $result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a ^ $b); + if ($result instanceof Type) { + return $result; + } elseif ($result === self::IS_SCALAR_TYPE) { $leftType = $this->optimizeScalarType($leftType); $rightType = $this->optimizeScalarType($rightType); } From 2834aa0123b421714a2593df1139d8a0e02424cd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 9 Feb 2026 14:33:48 +0100 Subject: [PATCH 3/3] simplify --- .../InitializerExprTypeResolver.php | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 5cb3c71a51..499f504a7a 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -1099,40 +1099,41 @@ private function getFiniteOrConstantScalarTypes(Type $leftType, Type $rightType, $leftTypesCount = count($leftTypes); $rightTypesCount = count($rightTypes); - if ($leftTypesCount > 0 && $rightTypesCount > 0) { - $resultTypes = []; - $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; - if (!$generalize) { - foreach ($leftTypes as $leftTypeInner) { - foreach ($rightTypes as $rightTypeInner) { - if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) { - $resultValue = $operationCallable($leftTypeInner->getValue(), $rightTypeInner->getValue()); - $resultType = $this->getTypeFromValue($resultValue); - } else { - $leftNumberType = $leftTypeInner->toNumber(); - $rightNumberType = $rightTypeInner->toNumber(); - if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) { - return new ErrorType(); - } + if ($leftTypesCount === 0 || $rightTypesCount === 0) { + return self::IS_UNKNOWN; + } - if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) { - throw new ShouldNotHappenException(); - } + $generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT; + if ($generalize) { + return self::IS_SCALAR_TYPE; + } - $resultValue = $operationCallable($leftNumberType->getValue(), $rightNumberType->getValue()); - $resultType = $this->getTypeFromValue($resultValue); - } - $resultTypes[] = $resultType; + $resultTypes = []; + foreach ($leftTypes as $leftTypeInner) { + foreach ($rightTypes as $rightTypeInner) { + if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) { + $resultValue = $operationCallable($leftTypeInner->getValue(), $rightTypeInner->getValue()); + $resultType = $this->getTypeFromValue($resultValue); + } else { + $leftNumberType = $leftTypeInner->toNumber(); + $rightNumberType = $rightTypeInner->toNumber(); + + if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) { + return new ErrorType(); + } + + if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) { + throw new ShouldNotHappenException(); } + + $resultValue = $operationCallable($leftNumberType->getValue(), $rightNumberType->getValue()); + $resultType = $this->getTypeFromValue($resultValue); } - return TypeCombinator::union(...$resultTypes); + $resultTypes[] = $resultType; } - - return self::IS_SCALAR_TYPE; } - - return self::IS_UNKNOWN; + return TypeCombinator::union(...$resultTypes); } public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type