From 9675cc85745a9ac2e65b123282b62d0cbabe43b0 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 3 Nov 2025 11:37:31 +0100 Subject: [PATCH 1/2] Add support for union of constant integer for filter_var --- src/Type/Php/FilterFunctionReturnTypeHelper.php | 10 ++++++++-- tests/PHPStan/Analyser/nsrt/filter-var.php | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Type/Php/FilterFunctionReturnTypeHelper.php b/src/Type/Php/FilterFunctionReturnTypeHelper.php index 1e920328bd..412fbb51d5 100644 --- a/src/Type/Php/FilterFunctionReturnTypeHelper.php +++ b/src/Type/Php/FilterFunctionReturnTypeHelper.php @@ -495,11 +495,17 @@ private function hasFlag(string $flagName, ?Type $flagsType): TrinaryLogic } $type = $this->getFlagsValue($flagsType); - if (!$type instanceof ConstantIntegerType) { + $scalarValues = $type->getConstantScalarValues(); + if ($scalarValues === []) { return TrinaryLogic::createMaybe(); } - return TrinaryLogic::createFromBoolean(($type->getValue() & $flag) === $flag); + return TrinaryLogic::lazyExtremeIdentity( + $scalarValues, + static fn (bool|string|int|float|null $scalar): TrinaryLogic => TrinaryLogic::createFromBoolean( + (((int) $scalar) & $flag) === $flag, + ), + ); } private function getFlagsValue(Type $exprType): Type diff --git a/tests/PHPStan/Analyser/nsrt/filter-var.php b/tests/PHPStan/Analyser/nsrt/filter-var.php index 1935c117aa..22a3d7ac83 100644 --- a/tests/PHPStan/Analyser/nsrt/filter-var.php +++ b/tests/PHPStan/Analyser/nsrt/filter-var.php @@ -169,4 +169,11 @@ public function scalars(bool $bool, float $float, int $int, string $string, int assertType("''", filter_var(null)); } + public function randomFlag($mixed, bool $bool) { + + assertType('int|false|null', filter_var($mixed, FILTER_VALIDATE_INT, [ + 'flags' => $bool ? FILTER_NULL_ON_FAILURE : FILTER_FLAG_NONE, + ])); + } + } From dc7ea0795fe95d1e8cd73953addd73c40c6a2012 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 3 Nov 2025 11:53:35 +0100 Subject: [PATCH 2/2] Fix --- src/Type/Php/FilterFunctionReturnTypeHelper.php | 2 +- tests/PHPStan/Analyser/nsrt/filter-var.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/FilterFunctionReturnTypeHelper.php b/src/Type/Php/FilterFunctionReturnTypeHelper.php index 412fbb51d5..98834338b5 100644 --- a/src/Type/Php/FilterFunctionReturnTypeHelper.php +++ b/src/Type/Php/FilterFunctionReturnTypeHelper.php @@ -191,7 +191,7 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T } if ($exactType === null || $hasOptions->maybe() || (!$inputType->equals($type) && $inputType->isSuperTypeOf($type)->yes())) { - if ($defaultType->isSuperTypeOf($type)->no()) { + if (!$defaultType->isSuperTypeOf($type)->yes()) { $type = TypeCombinator::union($type, $defaultType); } } diff --git a/tests/PHPStan/Analyser/nsrt/filter-var.php b/tests/PHPStan/Analyser/nsrt/filter-var.php index 22a3d7ac83..34d23f9c8f 100644 --- a/tests/PHPStan/Analyser/nsrt/filter-var.php +++ b/tests/PHPStan/Analyser/nsrt/filter-var.php @@ -174,6 +174,10 @@ public function randomFlag($mixed, bool $bool) { assertType('int|false|null', filter_var($mixed, FILTER_VALIDATE_INT, [ 'flags' => $bool ? FILTER_NULL_ON_FAILURE : FILTER_FLAG_NONE, ])); + + assertType('bool|null', filter_var($mixed, FILTER_VALIDATE_BOOLEAN, [ + 'flags' => $bool ? FILTER_NULL_ON_FAILURE : FILTER_FLAG_NONE, + ])); } }