From ea9daa508c20f41e4cb9a63c8bec6094586aa8cf Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:53:48 +0000 Subject: [PATCH 1/5] Fix isset() falsey branch not narrowing array type for dim fetches - In the falsey branch of isset($a['key']), narrow the array variable by removing union members where the key is definitely present with a non-null value - Preserve variable certainty when the array variable might not be defined (using IssetExpr to restore maybe certainty) - Update tagged-unions test expectation to reflect improved narrowing precision - New regression test in tests/PHPStan/Analyser/nsrt/bug-9908.php Closes https://github.com/phpstan/phpstan/issues/9908 --- phpstan-baseline.neon | 2 +- src/Analyser/TypeSpecifier.php | 46 +++++++++++++++++++ tests/PHPStan/Analyser/nsrt/bug-9908.php | 44 ++++++++++++++++++ tests/PHPStan/Analyser/nsrt/tagged-unions.php | 2 +- 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-9908.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f7b1c64ee0..a6d38c503c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -87,7 +87,7 @@ parameters: - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.' identifier: phpstanApi.instanceofType - count: 4 + count: 5 path: src/Analyser/TypeSpecifier.php - diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 3a1f8bd98e..9a577ee53f 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -920,6 +920,52 @@ public function specifyTypesInCondition( return $exprType; } + if ( + $issetExpr instanceof ArrayDimFetch + && $issetExpr->dim !== null + ) { + $varType = $scope->getType($issetExpr->var); + if (!$varType instanceof MixedType) { + $dimType = $scope->getType($issetExpr->dim); + + if ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) { + $types = $varType instanceof UnionType ? $varType->getTypes() : [$varType]; + $typesToRemove = []; + foreach ($types as $innerType) { + $hasOffset = $innerType->hasOffsetValueType($dimType); + if (!$hasOffset->yes() || !$innerType->getOffsetValueType($dimType)->isNull()->no()) { + continue; + } + + $typesToRemove[] = $innerType; + } + + if ($typesToRemove !== []) { + $typeToRemove = TypeCombinator::union(...$typesToRemove); + $result = $this->create( + $issetExpr->var, + $typeToRemove, + TypeSpecifierContext::createFalse(), + $scope, + )->setRootExpr($expr); + + if ($scope->hasExpressionType($issetExpr->var)->maybe()) { + $result = $result->unionWith( + $this->create( + new IssetExpr($issetExpr->var), + new NullType(), + TypeSpecifierContext::createTruthy(), + $scope, + )->setRootExpr($expr), + ); + } + + return $result; + } + } + } + } + return new SpecifiedTypes(); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-9908.php b/tests/PHPStan/Analyser/nsrt/bug-9908.php new file mode 100644 index 0000000000..d51c4c33f9 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-9908.php @@ -0,0 +1,44 @@ + 'string']; + } + + if (isset($a['bar'])) { + $a['bar'] = 1; + } + + assertType("array{}|array{bar: 1}", $a); + } + + /** + * @param array{bar?: int} $foo + */ + public function sayHello(array $foo): void + { + echo 'Hello' . print_r($foo, true); + } + + public function test2(): void + { + $a = []; + if (rand() % 2) { + $a = ['bar' => 'string']; + } + + if (isset($a['bar'])) { + $a['bar'] = 1; + } + + $this->sayHello($a); + } +} diff --git a/tests/PHPStan/Analyser/nsrt/tagged-unions.php b/tests/PHPStan/Analyser/nsrt/tagged-unions.php index 9926467123..45e7e6e98b 100644 --- a/tests/PHPStan/Analyser/nsrt/tagged-unions.php +++ b/tests/PHPStan/Analyser/nsrt/tagged-unions.php @@ -55,7 +55,7 @@ public function doFoo4(array $foo) if (isset($foo['C'])) { assertType("array{A: string, C: 1}", $foo); } else { - assertType("array{A: int, B: 1}|array{A: string, C: 1}", $foo); // could be array{A: int, B: 1} + assertType("array{A: int, B: 1}", $foo); } assertType('array{A: int, B: 1}|array{A: string, C: 1}', $foo); From 0e820757613ea7cd1118421196a665e65bba477a Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Mon, 16 Feb 2026 21:01:00 +0000 Subject: [PATCH 2/5] Add regression test for #10544 Closes https://github.com/phpstan/phpstan/issues/10544 --- tests/PHPStan/Analyser/nsrt/bug-10544.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-10544.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-10544.php b/tests/PHPStan/Analyser/nsrt/bug-10544.php new file mode 100644 index 0000000000..22b9ed2636 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-10544.php @@ -0,0 +1,19 @@ + Date: Mon, 16 Feb 2026 21:01:49 +0000 Subject: [PATCH 3/5] Add regression test for #8724 Closes https://github.com/phpstan/phpstan/issues/8724 --- tests/PHPStan/Analyser/nsrt/bug-8724.php | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-8724.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-8724.php b/tests/PHPStan/Analyser/nsrt/bug-8724.php new file mode 100644 index 0000000000..121c26bd49 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-8724.php @@ -0,0 +1,28 @@ + Date: Mon, 16 Feb 2026 21:02:38 +0000 Subject: [PATCH 4/5] Add regression test for #5128 Closes https://github.com/phpstan/phpstan/issues/5128 --- tests/PHPStan/Analyser/nsrt/bug-5128.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-5128.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-5128.php b/tests/PHPStan/Analyser/nsrt/bug-5128.php new file mode 100644 index 0000000000..163bace5a3 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-5128.php @@ -0,0 +1,18 @@ + Date: Mon, 16 Feb 2026 21:03:19 +0000 Subject: [PATCH 5/5] Add regression test for #12401 Closes https://github.com/phpstan/phpstan/issues/12401 --- tests/PHPStan/Analyser/nsrt/bug-12401.php | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-12401.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-12401.php b/tests/PHPStan/Analyser/nsrt/bug-12401.php new file mode 100644 index 0000000000..14155dc701 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12401.php @@ -0,0 +1,24 @@ +