From f9eec12aeb3969023f6e8c69ba64bb653a597174 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 07:12:35 +0200 Subject: [PATCH 1/6] Fix "Offset decimal-int-string does not exist on array." false positive --- src/Type/IntersectionType.php | 4 ++++ .../NonexistentOffsetInArrayDimFetchRuleTest.php | 5 +++++ tests/PHPStan/Rules/Arrays/data/bug-14758.php | 11 +++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/PHPStan/Rules/Arrays/data/bug-14758.php diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 98d07e1ad0..9078686c5f 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -2,6 +2,7 @@ namespace PHPStan\Type; +use PHPStan\DependencyInjection\ReportUnsafeArrayStringKeyCastingToggle; use PHPStan\Internal\CombinationsHelper; use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; @@ -1554,6 +1555,9 @@ public function toArray(): Type public function toArrayKey(): Type { if ($this->isDecimalIntegerString()->yes()) { + if (ReportUnsafeArrayStringKeyCastingToggle::getLevel() !== ReportUnsafeArrayStringKeyCastingToggle::PREVENT) { + return $this; + } return new IntegerType(); } diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 2927248dd8..3129028a6f 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1326,6 +1326,11 @@ public function testBug9240(): void $this->analyse([__DIR__ . '/data/bug-9240.php'], []); } + public function testBug14758(): void + { + $this->analyse([__DIR__ . '/data/bug-14758.php'], []); + } + #[RequiresPhp('>= 8.4.0')] public function testArrayFindKeyExisting(): void { diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14758.php b/tests/PHPStan/Rules/Arrays/data/bug-14758.php new file mode 100644 index 0000000000..25ae5ef33c --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-14758.php @@ -0,0 +1,11 @@ + $arr + */ +function doBar(string $s, $arr) { + if (ctype_digit($s)) { + var_dump($arr[$s]); + } +} From 774c6a525c9628ec7aea32eb68d51e7edc54e2f5 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 07:46:44 +0200 Subject: [PATCH 2/6] fix --- tests/PHPStan/Analyser/nsrt/decimal-int-string.php | 2 +- tests/PHPStan/Rules/Arrays/data/bug-14758.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/nsrt/decimal-int-string.php b/tests/PHPStan/Analyser/nsrt/decimal-int-string.php index fe5c5fdd52..f59f41bba7 100644 --- a/tests/PHPStan/Analyser/nsrt/decimal-int-string.php +++ b/tests/PHPStan/Analyser/nsrt/decimal-int-string.php @@ -14,7 +14,7 @@ public function doFoo(string $s): void { assertType('decimal-int-string' ,$s); $a = [$s => 1]; - assertType('non-empty-array', $a); + assertType('non-empty-array', $a); assertType('bool', (bool) $s); diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14758.php b/tests/PHPStan/Rules/Arrays/data/bug-14758.php index 25ae5ef33c..725ad22051 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-14758.php +++ b/tests/PHPStan/Rules/Arrays/data/bug-14758.php @@ -1,5 +1,7 @@ $arr From a37479ed8f6a9681c33e06b9246a04d040105f41 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 09:30:11 +0200 Subject: [PATCH 3/6] Update bug-14758.php --- tests/PHPStan/Rules/Arrays/data/bug-14758.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14758.php b/tests/PHPStan/Rules/Arrays/data/bug-14758.php index 725ad22051..059e1ec150 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-14758.php +++ b/tests/PHPStan/Rules/Arrays/data/bug-14758.php @@ -6,7 +6,7 @@ * @param string $s * @param array $arr */ -function doBar(string $s, $arr) { +function doBarBug14758(string $s, $arr) { if (ctype_digit($s)) { var_dump($arr[$s]); } From f879b942d333df617469c4ffe5540729831d9a44 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 09:47:16 +0200 Subject: [PATCH 4/6] change expectations --- src/Type/IntersectionType.php | 4 ---- tests/PHPStan/Analyser/nsrt/decimal-int-string.php | 2 +- .../Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php | 7 ++++++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 9078686c5f..98d07e1ad0 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -2,7 +2,6 @@ namespace PHPStan\Type; -use PHPStan\DependencyInjection\ReportUnsafeArrayStringKeyCastingToggle; use PHPStan\Internal\CombinationsHelper; use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; @@ -1555,9 +1554,6 @@ public function toArray(): Type public function toArrayKey(): Type { if ($this->isDecimalIntegerString()->yes()) { - if (ReportUnsafeArrayStringKeyCastingToggle::getLevel() !== ReportUnsafeArrayStringKeyCastingToggle::PREVENT) { - return $this; - } return new IntegerType(); } diff --git a/tests/PHPStan/Analyser/nsrt/decimal-int-string.php b/tests/PHPStan/Analyser/nsrt/decimal-int-string.php index f59f41bba7..fe5c5fdd52 100644 --- a/tests/PHPStan/Analyser/nsrt/decimal-int-string.php +++ b/tests/PHPStan/Analyser/nsrt/decimal-int-string.php @@ -14,7 +14,7 @@ public function doFoo(string $s): void { assertType('decimal-int-string' ,$s); $a = [$s => 1]; - assertType('non-empty-array', $a); + assertType('non-empty-array', $a); assertType('bool', (bool) $s); diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 3129028a6f..5919641a72 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1328,7 +1328,12 @@ public function testBug9240(): void public function testBug14758(): void { - $this->analyse([__DIR__ . '/data/bug-14758.php'], []); + $this->analyse([__DIR__ . '/data/bug-14758.php'], [ + [ + 'Offset decimal-int-string does not exist on array.', + 9 + ] + ]); } #[RequiresPhp('>= 8.4.0')] From 468edb044a724fddd60e248a757cfbbe63143147 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 09:48:16 +0200 Subject: [PATCH 5/6] Update NonexistentOffsetInArrayDimFetchRuleTest.php --- .../Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 5919641a72..58ebbd69e4 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1331,8 +1331,8 @@ public function testBug14758(): void $this->analyse([__DIR__ . '/data/bug-14758.php'], [ [ 'Offset decimal-int-string does not exist on array.', - 9 - ] + 9, + ], ]); } From 78da3010e2c40be22d39b98f68eae132083d9402 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2026 09:51:44 +0200 Subject: [PATCH 6/6] fix --- .../NonexistentOffsetInArrayDimFetchRuleTest.php | 2 +- tests/PHPStan/Rules/Arrays/data/bug-14758.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 58ebbd69e4..a87b9eaedd 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1331,7 +1331,7 @@ public function testBug14758(): void $this->analyse([__DIR__ . '/data/bug-14758.php'], [ [ 'Offset decimal-int-string does not exist on array.', - 9, + 11, ], ]); } diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14758.php b/tests/PHPStan/Rules/Arrays/data/bug-14758.php index 059e1ec150..936a093ff0 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-14758.php +++ b/tests/PHPStan/Rules/Arrays/data/bug-14758.php @@ -11,3 +11,13 @@ function doBarBug14758(string $s, $arr) { var_dump($arr[$s]); } } + +/** + * @param string $s + * @param array $arr + */ +function doBarBug14758IntKeyed(string $s, $arr) { + if (ctype_digit($s)) { + var_dump($arr[$s]); + } +}