Drop false from array_search(..., true) when every finite needle value is guaranteed in the haystack#5947
Open
phpstan-bot wants to merge 1 commit into
Open
Conversation
…value is guaranteed in the haystack - ConstantArrayType::searchArray: generalize the "guaranteed found" detection to finite union and enum-case needles via getFiniteTypes() in strict mode, instead of only recognizing a single ConstantScalarType needle. When every possible needle value is guaranteed under a non-optional key, false is dropped from the result, so `array_search($needle, $a, true) !== false` is reported as always true. - IntersectionType::searchArray: for a non-empty general (non-constant) array whose values all share a single finite type that is a supertype of every finite needle value, remove false from the searchArray result (mirrors the non-empty-array handling for in_array). - HasOffsetValueType::searchArray: use getFiniteTypes()/isSuperTypeOf() instead of a `ConstantScalarType` + `===` check so a known-offset value also recognizes enum-case (and other finite, non-scalar) needles as guaranteed found; the loose-equality scalar branch is preserved. - Add type-inference regression test (nsrt/bug-14877) and a StrictComparisonOfDifferentTypesRule test (data/bug-14877) covering constant arrays, enums, integers, known offsets and general arrays.
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
array_search($needle, $haystack, true)returns the matching key orfalse. When the needle is known to take a finite set of values that is a subset of the haystack's guaranteed values, the search can never returnfalse, so a check likearray_search($needle, $a, true) !== falseis always true. PHPStan keptfalsein the return type whenever the needle was a union of finite values (or an enum case), so it failed to report the always-true comparison. This mirrors the recently fixedin_array(..., true)case (#14873).This change drops
falsefrom thearray_search()result whenever every possible finite needle value is guaranteed to be present, which lets the strict-comparison rule report the always-true!== false.Changes
src/Type/Constant/ConstantArrayType.php—searchArray()now detects "guaranteed found" for finite union and enum-case needles viagetFiniteTypes()(new privateneedleIsGuaranteedToBeFound()helper), not just for a singleConstantScalarType. A needle is guaranteed found when every one of its finite values is a subtype of some non-optional value that is itself a single finite type.src/Type/IntersectionType.php—searchArray()removesfalsefrom the intersected result for a non-empty general array whose iterable value type is a single finite type that is a supertype of every finite needle value (e.g.non-empty-array<int, 1>searched for1). Mirrors the non-empty-array handling thatin_arrayalready had.src/Type/Accessory/HasOffsetValueType.php—searchArray()now usesgetFiniteTypes()/isSuperTypeOf()rather thaninstanceof ConstantScalarType+===, so a known offset value also recognizes enum-case (and other finite, non-scalar) needles as guaranteed found. The loose-equality scalar branch is kept for non-strict comparisons.Probed and found already correct (no change needed):
ArrayType::searchArray(plain, possibly-empty arrays cannot guarantee a value),UnionType::searchArray(delegates per-member, sofalseremoval already composes), and theAccessoryArrayListType/OversizedArrayType/NonEmptyArrayTypeaccessory members (their results are combined throughIntersectionType::searchArray).Root cause
The "needle is guaranteed present, so the result has no
false" logic was expressed only in terms of a singleConstantScalarTypeneedle. The general pattern is finite types: a needle that can only be one of finitely many values is guaranteed found when each of those values is guaranteed under a non-optional key. ThreesearchArray()implementations encoded the narrow scalar-only form of this check:ConstantArrayTypeonly sethasIdenticalValuefor a single matching constant scalar.false).HasOffsetValueTypecheckedinstanceof ConstantScalarType+getValue() === getValue(), missing enum cases.The fix replaces these with the finite-types formulation (
getFiniteTypes()+isSuperTypeOf()), in line with the codebase rule of querying type properties viaTypemethods rather thaninstanceof.Test
tests/PHPStan/Analyser/nsrt/bug-14877.php—assertType()coverage forarray_search(..., true)over constant arrays (full/subset/partial finite-union string, integer and enum needles), non-strict search, general arrays (maybe-empty, non-empty multi-value, non-empty single-value), plain objects (no finite values,falseretained), and known-offset (HasOffsetValueType) enum/scalar needles.tests/PHPStan/Rules/Comparison/data/bug-14877.php+StrictComparisonOfDifferentTypesRuleTest::testBug14877— assertsarray_search(..., true) !== falseis reported as always-true for the guaranteed cases and not reported for partial/maybe-empty/multi-value cases.Both tests fail before the fix and pass after it.
Fixes phpstan/phpstan#14877