Repository navigation
Ignore FILTER_THROW_ON_FAILURE bit when the PHP version range only maybe includes PHP 8.5 and the runtime FILTER_FLAG_GLOBAL_RANGE shares it - #6779
Open
phpstan-bot wants to merge 5 commits into
Conversation
VincentLanglet
force-pushed
the
create-pull-request/patch-tt8irro
branch
2 times, most recently
from
October 23, 2026 09:47
8103943 to
ea26c30
Compare
…maybe includes PHP 8.5 and the runtime `FILTER_FLAG_GLOBAL_RANGE` shares it
- FilterFunctionReturnTypeHelper::hasThrowOnFailureFlag() returns no when
PhpVersions::hasFilterThrowOnFailureConstant() is maybe and the stubbed
FILTER_THROW_ON_FAILURE value equals the runtime FILTER_FLAG_GLOBAL_RANGE
value (PHP < 8.5 runtime, 0x10000000). When the scope is definitely PHP 8.5+,
the flag is still detected as before.
- FilterFunctionsThrowTypeExtension now uses the same hasThrowOnFailureFlag()
helper, so FILTER_FLAG_GLOBAL_RANGE is no longer reported as throwing
Filter\FilterFailedException on such ranges.
- Probed FilterVarRule ("Cannot use both FILTER_NULL_ON_FAILURE and
FILTER_THROW_ON_FAILURE"): it uses the single configured PhpVersion, which is
below 8.5 for a range like 8.4-8.5, so it was not affected.
When the PHP version range only maybe includes PHP 8.5 and the runtime FILTER_FLAG_GLOBAL_RANGE shares its bit, the set bit may mean either flag. Returning no made a catch of Filter\FilterFailedException around a real FILTER_THROW_ON_FAILURE call reported as dead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re a value Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heck Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VincentLanglet
force-pushed
the
create-pull-request/patch-tt8irro
branch
from
October 23, 2026 10:55
ea26c30 to
b30c6e1
Compare
staabm
reviewed
Oct 23, 2026
|
|
||
| foreach ($this->filterFunctionFlagsHelper->getFlagsTypes($functionReflection, $funcCall, $scope) as $flagsType) { | ||
| if ($this->filterFunctionReturnTypeHelper->hasFlag('FILTER_THROW_ON_FAILURE', $flagsType)->no()) { | ||
| if ($this->filterFunctionReturnTypeHelper->hasThrowOnFailureFlag($flagsType, $scope->getPhpVersion())->no()) { |
Contributor
There was a problem hiding this comment.
move $scope->getPhpVersion() before the loop
Collaborator
Author
There was a problem hiding this comment.
I moved the $scope->getPhpVersion() call before the loop and pushed the change as commit e6f5722f9. make tests passes (22562 tests, 106 skipped) and make phpstan reports no errors.
In FilterFunctionsThrowTypeExtension::getThrowTypeFromFunctionCall(), the PHP versions are now read once into $phpVersions at the top. The early-return check before the loop and the hasThrowOnFailureFlag() call inside it both use that variable, so getPhpVersion() is no longer called on every pass through the loop. Nothing else changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When PHPStan runs on PHP < 8.5 and the analysed PHP version range only maybe reaches 8.5 (e.g.
"php": "^8.4"in composer.json →int<80400, 80699>),FILTER_FLAG_GLOBAL_RANGEwas read asFILTER_THROW_ON_FAILURE.filter_var()lostfalsefrom its return type and comparisons againstfalsewere reported as always true. This was a regression from #6551, which moved the version check toScope::getPhpVersion(): for a range, the check became maybe, sohasFlag()ran.Changes
src/Type/Php/FilterFunctionReturnTypeHelper.php:hasThrowOnFailureFlag()is now public. It returns no when the scope's PHP versions only maybe haveFILTER_THROW_ON_FAILUREand the bit is ambiguous, which means the stubbedFILTER_THROW_ON_FAILUREvalue equals the runtimeFILTER_FLAG_GLOBAL_RANGEvalue. If the scope is definitely PHP 8.5+ (e.g. insideif (PHP_VERSION_ID >= 80500)), the flag is still detected.src/Type/Php/FilterFunctionsThrowTypeExtension.php: uses the same helper instead of a barehasFlag('FILTER_THROW_ON_FAILURE'). This fixes the sibling bug whereFILTER_FLAG_GLOBAL_RANGEwas treated as throwingFilter\FilterFailedException, so a catch for it was not reported as dead.FilterVarRule("Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE"). It is gated on the single configuredPhpVersion, which stays below 8.5 for such ranges, so it was not affected in the reported scenario.Root cause
PHPStan resolves internal constant values from the runtime. On PHP < 8.5,
FILTER_FLAG_GLOBAL_RANGEis0x10000000. PHP 8.5 moved it to0x20000000and gave0x10000000toFILTER_THROW_ON_FAILURE, which comes from stubs on older runtimes. A bitmask test therefore cannot tell the two flags apart on such a runtime. Before #6551, the version check for a range said "no". Afterwards it said "maybe", and the bitmask test ran anyway. Both consumers of the throw-on-failure check (the return type helper and the throw type extension) now go through one guarded method.Test
tests/PHPStan/Analyser/data/scope-php-version-range-return-type-extensions.php(range 7.4–8.5):filter_var/filter_input_arraycalls withFILTER_FLAG_GLOBAL_RANGE, including the issue's combined flags, are expected to keepfalse(ornullwithFILTER_NULL_ON_FAILURE).tests/PHPStan/Rules/Exceptions/data/throw-type-php-versions.php(range 8.0–8.5): catchingFilter\FilterFailedExceptionaroundfilter_var(..., FILTER_FLAG_GLOBAL_RANGE)is reported as a dead catch.intforFILTER_THROW_ON_FAILUREinsidePHP_VERSION_ID >= 80500) still passes.Fixes phpstan/phpstan#15481