Narrow filter_var first argument type via FunctionTypeSpecifyingExtension when validation filter passes#5512
Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Closed
Conversation
…tension` when validation filter passes - Add `FilterVarTypeSpecifyingExtension` implementing `FunctionTypeSpecifyingExtension` to narrow the first argument of `filter_var()` when the function result is truthy (validation passed) - Add `getInputNarrowingType()` to `FilterFunctionReturnTypeHelper` to determine the appropriate narrowing type based on the filter constant and flags - Add delegation in `TypeSpecifier::resolveNormalizedIdentical` for `filter_var(...) === false` pattern to propagate narrowing from the comparison to the function argument - Covers FILTER_VALIDATE_EMAIL, FILTER_VALIDATE_IP, FILTER_VALIDATE_URL, FILTER_VALIDATE_MAC (all narrow to non-falsy-string) - Correctly does NOT narrow for FILTER_VALIDATE_DOMAIN, FILTER_VALIDATE_REGEXP (return type is just string), FILTER_VALIDATE_INT/FLOAT/BOOLEAN (return type is not string), FILTER_DEFAULT, and sanitize filters
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
filter_var($email, FILTER_VALIDATE_EMAIL)is used in a condition without assignment (e.g.,if (false === filter_var($email, FILTER_VALIDATE_EMAIL))), the original$emailvariable was not narrowed. This PR adds type narrowing for the first argument offilter_var()when a validation filter passes, covering all common comparison patterns.Changes
New
FilterVarTypeSpecifyingExtension(src/Type/Php/FilterVarTypeSpecifyingExtension.php): ImplementsFunctionTypeSpecifyingExtensionto narrow the first argument whenfilter_var()is used in a truthy context. Uses the existingFilterFunctionReturnTypeHelperto determine which filters guarantee non-empty/non-falsy string input.New
getInputNarrowingType()method onFilterFunctionReturnTypeHelper(src/Type/Php/FilterFunctionReturnTypeHelper.php): Computes the return type for a generic string input via the existinggetType()method, strips failure types (false/null), and returns the appropriate accessory type (non-empty-string or non-falsy-string) if the successful return type is a refined string.TypeSpecifier delegation (
src/Analyser/TypeSpecifier.php): Added a case inresolveNormalizedIdenticalforfilter_var(...) === falsethat delegates to the function type specifying extension with the appropriate context. This makes patterns likefalse === filter_var(...)andfilter_var(...) !== falsetrigger the narrowing.Filters that narrow the input
FILTER_VALIDATE_EMAIL→ narrows tonon-falsy-stringFILTER_VALIDATE_IP→ narrows tonon-falsy-stringFILTER_VALIDATE_URL→ narrows tonon-falsy-stringFILTER_VALIDATE_MAC→ narrows tonon-falsy-stringFilters that correctly do NOT narrow
FILTER_VALIDATE_DOMAIN— return type in filter map isstring(not non-empty)FILTER_VALIDATE_REGEXP— could match empty stringFILTER_VALIDATE_INT,FILTER_VALIDATE_FLOAT,FILTER_VALIDATE_BOOLEAN— return non-string typesFILTER_DEFAULTand sanitize filters — not validation filtersRoot cause
There was a
DynamicFunctionReturnTypeExtensionforfilter_varthat correctly typed the return value (e.g.,non-falsy-string|false), but noFunctionTypeSpecifyingExtensionto propagate type narrowing back to the function's arguments when used in a condition. Additionally, the TypeSpecifier'sresolveNormalizedIdenticalhad no case forfilter_var(...) === false, so even with the extension, the=== falsecomparison pattern would not trigger it.Test
Comprehensive NSRT test at
tests/PHPStan/Analyser/nsrt/bug-14486.phpcovering:false === filter_var($email, FILTER_VALIDATE_EMAIL)+ throw (the exact issue pattern)filter_var($email, ...) !== falsecomparisonif (filter_var($email, ...))direct truthy checkif (!filter_var($email, ...))negated checkFixes phpstan/phpstan#14486