Skip to content

Fix phpstan/phpstan#14397: FILTER_FLAG_GLOBAL_RANGE constant not "known" on PHP 8.3, 8.4#5334

Merged
VincentLanglet merged 9 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-yzr5t1i
Mar 30, 2026
Merged

Fix phpstan/phpstan#14397: FILTER_FLAG_GLOBAL_RANGE constant not "known" on PHP 8.3, 8.4#5334
VincentLanglet merged 9 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-yzr5t1i

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

On PHP 8.3/8.4, using FILTER_FLAG_GLOBAL_RANGE with filter_var caused a false positive: "Strict comparison using === between non-falsy-string and false will always evaluate to false." This happened because FILTER_FLAG_GLOBAL_RANGE (268435456) shares the same numeric value as FILTER_THROW_ON_FAILURE on those PHP versions, causing PHPStan to incorrectly infer the function would throw instead of returning false.

Changes

  • Gate FILTER_THROW_ON_FAILURE checks behind PHP >= 8.5 in src/Type/Php/FilterFunctionReturnTypeHelper.php (two locations)
  • Add PHP version check to src/Type/Php/FilterVarThrowTypeExtension.php in isFunctionSupported()
  • Add PHP version check to src/Rules/Functions/FilterVarRule.php for the null-on-failure + throw-on-failure conflict detection

Root cause

The JetBrains PHP stubs define FILTER_THROW_ON_FAILURE with value 268435456 without any PHP version guard. On PHP 8.3/8.4, FILTER_FLAG_GLOBAL_RANGE also has value 268435456 (changed to 536870912 in PHP 8.5 to avoid this exact collision). When PHPStan checked whether FILTER_THROW_ON_FAILURE was present in the flags via bitwise AND, it matched FILTER_FLAG_GLOBAL_RANGE and incorrectly removed false from the return type, since throw-on-failure means the function throws instead of returning a failure value.

Test

Added tests/PHPStan/Analyser/nsrt/bug-14397.php — asserts that filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_GLOBAL_RANGE) returns non-falsy-string|false (not just non-falsy-string).

Fixes phpstan/phpstan#14397

@spaze
Copy link
Copy Markdown
Contributor

spaze commented Mar 29, 2026

Oh I see now, PHP 8.5 has introduced FILTER_THROW_ON_FAILURE and they put it in the middle of the flags bitmask moving the existing FILTER_FLAG_GLOBAL_RANGE to another bit. Not sure it was necessary though https://github.com/php/php-src/pull/18896/changes#diff-907132129e0423785307f3458a266f347c35b2899eebf77c8981fef38409e8a8

And JetBrains stubs list them with the same value
https://github.com/JetBrains/phpstorm-stubs/blob/3327932472f512d2eb9e122b19702b335083fd9d/filter/filter.php#L531-L538

Thanks for the fix! :-)

… PHP 8.3/8.4

- FILTER_FLAG_GLOBAL_RANGE (268435456) has the same numeric value as FILTER_THROW_ON_FAILURE on PHP 8.3/8.4
- This caused PHPStan to incorrectly think filter_var would throw, removing false from the return type
- Gate all FILTER_THROW_ON_FAILURE checks behind PHP >= 8.5 (where the constant was introduced)
- Applied fix to FilterFunctionReturnTypeHelper, FilterVarThrowTypeExtension, and FilterVarRule
- Updated FilterVarRuleTest to pass new PhpVersion constructor parameter
- New regression test in tests/PHPStan/Analyser/nsrt/bug-14397.php
@VincentLanglet VincentLanglet force-pushed the create-pull-request/patch-yzr5t1i branch from 5a53072 to fa75094 Compare March 30, 2026 09:52
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace the check

$this->phpVersion->getVersionId() >= 80500

by a dedicated method inside phpVersion.

hasFilterThrowOnFailureConstant()

…stant()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

Done. Added hasFilterThrowOnFailureConstant() method to PhpVersion and replaced all three raw getVersionId() >= 80500 checks in FilterVarRule, FilterFunctionReturnTypeHelper, and FilterVarThrowTypeExtension. Tests and static analysis both pass.

phpstan-bot and others added 4 commits March 30, 2026 10:11
- Add nsrt test verifying filter_var with FILTER_THROW_ON_FAILURE still
  returns false on PHP < 8.5 (throw type extension correctly inactive)
- Add FilterVarRule test verifying no error for FILTER_NULL_ON_FAILURE +
  FILTER_THROW_ON_FAILURE combination on PHP < 8.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@VincentLanglet VincentLanglet requested a review from staabm March 30, 2026 11:20
@@ -0,0 +1,14 @@
<?php // lint < 8.5

namespace FilterVarNullAndThrowPrePhp85;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we test this file on 8.5+ and pre-8.5?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added a test (testRuleGlobalRangePhp85) that verifies FILTER_FLAG_GLOBAL_RANGE|FILTER_NULL_ON_FAILURE produces no false positive on PHP 8.5+, complementing the existing pre-8.5 test. All tests (11701) and static analysis pass. Changes pushed.

VincentLanglet and others added 2 commits March 30, 2026 13:40
Ensures FILTER_FLAG_GLOBAL_RANGE|FILTER_NULL_ON_FAILURE does not falsely
trigger the "cannot use both" error on PHP 8.5+ where the constants have
distinct values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
return [];
}

if (count($args) < 3) {
Copy link
Copy Markdown
Contributor

@staabm staabm Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should count args right after if (!($node->name instanceof Node\Name)) { to exit early with a cheap check

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests also confirmed passing. Everything's been pushed — the review comment from staabm has been addressed.

Copy link
Copy Markdown
Contributor

@staabm staabm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

…checks in FilterVarRule

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@VincentLanglet VincentLanglet merged commit 6f4489a into phpstan:2.1.x Mar 30, 2026
652 of 655 checks passed
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-yzr5t1i branch March 30, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants