From c990cdd6d43258d9993bbc5fffa128eb8005e701 Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:30:39 +0000 Subject: [PATCH 1/4] Fix phpstan/phpstan#14312: array_key_exists narrowing non-empty-array not accepted as array - Fixed ArrayKeyExistsFunctionTypeSpecifyingExtension to include ArrayType in the narrowing type when the variable is not already an array type - When mixed is narrowed via array_key_exists with a non-constant key, the type now becomes array & non-empty-array instead of standalone non-empty-array, which is properly accepted by the array parameter type - Added regression test in tests/PHPStan/Rules/Functions/data/bug-14312.php --- ...ayKeyExistsFunctionTypeSpecifyingExtension.php | 5 ++++- .../CallToFunctionParametersRuleTest.php | 5 +++++ tests/PHPStan/Rules/Functions/data/bug-14312.php | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-14312.php diff --git a/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php b/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php index ae9afb30055..ffbdc4069fb 100644 --- a/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php +++ b/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php @@ -78,9 +78,12 @@ public function specifyTypes( $specifiedTypes = new SpecifiedTypes(); if (count($keyType->getConstantScalarTypes()) <= 1) { + $nonEmptyType = $arrayType->isArray()->yes() + ? new NonEmptyArrayType() + : TypeCombinator::intersect(new ArrayType(new MixedType(), new MixedType()), new NonEmptyArrayType()); $specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create( $array, - new NonEmptyArrayType(), + $nonEmptyType, $context, $scope, )); diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 6bb50169fe6..676795fc041 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2746,4 +2746,9 @@ public function testBug13247(): void $this->analyse([__DIR__ . '/data/bug-13247.php'], []); } + public function testBug14312(): void + { + $this->analyse([__DIR__ . '/data/bug-14312.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-14312.php b/tests/PHPStan/Rules/Functions/data/bug-14312.php new file mode 100644 index 00000000000..c07ceac0bbf --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-14312.php @@ -0,0 +1,15 @@ + Date: Tue, 17 Mar 2026 15:33:05 +0000 Subject: [PATCH 2/4] Fix same NonEmptyArrayType narrowing issue in ArraySearchFunctionTypeSpecifyingExtension Apply the same fix from ArrayKeyExistsFunctionTypeSpecifyingExtension: when the variable is not already an array type, include ArrayType(mixed, mixed) in the intersection to produce a proper array & non-empty-array type. Co-Authored-By: Claude Opus 4.6 --- ...ArraySearchFunctionTypeSpecifyingExtension.php | 10 +++++++++- .../CallToFunctionParametersRuleTest.php | 5 +++++ tests/PHPStan/Rules/Functions/data/bug-14312b.php | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-14312b.php diff --git a/src/Type/Php/ArraySearchFunctionTypeSpecifyingExtension.php b/src/Type/Php/ArraySearchFunctionTypeSpecifyingExtension.php index 38d8909e9b1..2d8e2ceaf69 100644 --- a/src/Type/Php/ArraySearchFunctionTypeSpecifyingExtension.php +++ b/src/Type/Php/ArraySearchFunctionTypeSpecifyingExtension.php @@ -11,7 +11,10 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\NonEmptyArrayType; +use PHPStan\Type\ArrayType; use PHPStan\Type\FunctionTypeSpecifyingExtension; +use PHPStan\Type\MixedType; +use PHPStan\Type\TypeCombinator; use function strtolower; #[AutowiredService] @@ -42,9 +45,14 @@ public function specifyTypes( return new SpecifiedTypes(); } + $arrayType = $scope->getType($arrayArg); + $nonEmptyType = $arrayType->isArray()->yes() + ? new NonEmptyArrayType() + : TypeCombinator::intersect(new ArrayType(new MixedType(), new MixedType()), new NonEmptyArrayType()); + return $this->typeSpecifier->create( $arrayArg, - new NonEmptyArrayType(), + $nonEmptyType, $context, $scope, ); diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 676795fc041..37cf97c973a 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2751,4 +2751,9 @@ public function testBug14312(): void $this->analyse([__DIR__ . '/data/bug-14312.php'], []); } + public function testBug14312b(): void + { + $this->analyse([__DIR__ . '/data/bug-14312b.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-14312b.php b/tests/PHPStan/Rules/Functions/data/bug-14312b.php new file mode 100644 index 00000000000..01ce5daa8ee --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-14312b.php @@ -0,0 +1,15 @@ + Date: Tue, 17 Mar 2026 16:48:43 +0100 Subject: [PATCH 3/4] fix test --- tests/PHPStan/Rules/Functions/data/bug-14312b.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Functions/data/bug-14312b.php b/tests/PHPStan/Rules/Functions/data/bug-14312b.php index 01ce5daa8ee..7909f6a4b61 100644 --- a/tests/PHPStan/Rules/Functions/data/bug-14312b.php +++ b/tests/PHPStan/Rules/Functions/data/bug-14312b.php @@ -7,7 +7,7 @@ function get_something(): mixed { return null; } function test(string $needle): bool { $o = get_something(); if (array_search($needle, $o) !== false) { - if (array_key_exists($needle, $o)) { + if (array_search($needle, $o)) { return true; } } From 042e62563659a49fba5ca255970ddf0cd450fc5c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 17 Mar 2026 16:54:14 +0100 Subject: [PATCH 4/4] Update CallToFunctionParametersRuleTest.php --- .../Rules/Functions/CallToFunctionParametersRuleTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 37cf97c973a..f78f5918f77 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2746,11 +2746,13 @@ public function testBug13247(): void $this->analyse([__DIR__ . '/data/bug-13247.php'], []); } + #[RequiresPhp('>= 8.0')] public function testBug14312(): void { $this->analyse([__DIR__ . '/data/bug-14312.php'], []); } + #[RequiresPhp('>= 8.0')] public function testBug14312b(): void { $this->analyse([__DIR__ . '/data/bug-14312b.php'], []);