diff --git a/src/Type/LooseComparisonHelper.php b/src/Type/LooseComparisonHelper.php index b4df432c6f..275c974921 100644 --- a/src/Type/LooseComparisonHelper.php +++ b/src/Type/LooseComparisonHelper.php @@ -9,6 +9,17 @@ final class LooseComparisonHelper { public static function compareConstantScalars(ConstantScalarType $leftType, ConstantScalarType $rightType, PhpVersion $phpVersion): BooleanType + { + [$leftValue, $rightValue] = self::getConstantScalarValuesForComparison($leftType, $rightType, $phpVersion); + + // @phpstan-ignore equal.notAllowed + return new ConstantBooleanType($leftValue == $rightValue); // phpcs:ignore + } + + /** + * @return array{scalar, scalar} + */ + public static function getConstantScalarValuesForComparison(ConstantScalarType $leftType, ConstantScalarType $rightType, PhpVersion $phpVersion): array { if ($phpVersion->castsNumbersToStringsOnLooseComparison()) { $isNumber = new UnionType([ @@ -17,34 +28,27 @@ public static function compareConstantScalars(ConstantScalarType $leftType, Cons ]); if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $isNumber->isSuperTypeOf($rightType)->yes()) { - $stringValue = (string) $rightType->getValue(); - return new ConstantBooleanType($stringValue === $leftType->getValue()); + return [$leftType->getValue(), (string) $rightType->getValue()]; } if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $isNumber->isSuperTypeOf($leftType)->yes()) { - $stringValue = (string) $leftType->getValue(); - return new ConstantBooleanType($stringValue === $rightType->getValue()); + return [(string) $leftType->getValue(), $rightType->getValue()]; } } else { if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $rightType->isFloat()->yes()) { - $numericPart = (float) $leftType->getValue(); - return new ConstantBooleanType($numericPart === $rightType->getValue()); + return [(float) $leftType->getValue(), $rightType->getValue()]; } if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $leftType->isFloat()->yes()) { - $numericPart = (float) $rightType->getValue(); - return new ConstantBooleanType($numericPart === $leftType->getValue()); + return [$leftType->getValue(), (float) $rightType->getValue()]; } if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $rightType->isInteger()->yes()) { - $numericPart = (int) $leftType->getValue(); - return new ConstantBooleanType($numericPart === $rightType->getValue()); + return [(int) $leftType->getValue(), $rightType->getValue()]; } if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $leftType->isInteger()->yes()) { - $numericPart = (int) $rightType->getValue(); - return new ConstantBooleanType($numericPart === $leftType->getValue()); + return [$leftType->getValue(), (int) $rightType->getValue()]; } } - // @phpstan-ignore equal.notAllowed - return new ConstantBooleanType($leftType->getValue() == $rightType->getValue()); // phpcs:ignore + return [$leftType->getValue(), $rightType->getValue()]; } } diff --git a/src/Type/Traits/ConstantScalarTypeTrait.php b/src/Type/Traits/ConstantScalarTypeTrait.php index 7452cd3c83..3972f15802 100644 --- a/src/Type/Traits/ConstantScalarTypeTrait.php +++ b/src/Type/Traits/ConstantScalarTypeTrait.php @@ -81,7 +81,9 @@ public function equals(Type $type): bool public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic { if ($otherType instanceof ConstantScalarType) { - return TrinaryLogic::createFromBoolean($this->value < $otherType->getValue()); + [$leftValue, $rightValue] = LooseComparisonHelper::getConstantScalarValuesForComparison($this, $otherType, $phpVersion); + + return TrinaryLogic::createFromBoolean($leftValue < $rightValue); } if ($otherType instanceof CompoundType) { @@ -94,7 +96,9 @@ public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryL public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic { if ($otherType instanceof ConstantScalarType) { - return TrinaryLogic::createFromBoolean($this->value <= $otherType->getValue()); + [$leftValue, $rightValue] = LooseComparisonHelper::getConstantScalarValuesForComparison($this, $otherType, $phpVersion); + + return TrinaryLogic::createFromBoolean($leftValue <= $rightValue); } if ($otherType instanceof CompoundType) { diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 2ea23e1e59..3c0797dfd0 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -25,10 +25,17 @@ class NodeScopeResolverTest extends TypeInferenceTestCase */ private static function findTestFiles(): iterable { + yield __DIR__ . '/data/bug-11732-php74.php'; return; foreach (self::findTestDataFilesFromDirectory(__DIR__ . '/nsrt') as $testFile) { yield $testFile; } + if (PHP_VERSION_ID < 80000) { + yield __DIR__ . '/data/bug-11732-php8.php'; + } else { + yield __DIR__ . '/data/bug-11732-php74.php'; + } + if (PHP_VERSION_ID < 80200 && PHP_VERSION_ID >= 80100) { yield __DIR__ . '/data/enum-reflection-php81.php'; } diff --git a/tests/PHPStan/Analyser/data/bug-11732-php74.php b/tests/PHPStan/Analyser/data/bug-11732-php74.php new file mode 100644 index 0000000000..16da3821ef --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-11732-php74.php @@ -0,0 +1,34 @@ +