From d98ba23371615e0cd4ca9af8d29b038de98f1011 Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:14:31 +0000 Subject: [PATCH 1/2] Fix internal error in ConstantArrayTypeBuilder when adding optional integer keys from union offsets - Fixed nextAutoIndexes not being updated when adding optional integer keys from scalar type decomposition in setOffsetValueType - The crash occurred when array_search() result (string|false) was used as an array offset, and false->toArrayKey() produced ConstantIntegerType(0) that wasn't tracked in nextAutoIndexes - Added regression test in tests/PHPStan/Rules/Arrays/data/bug-14305.php - Updated baseline count for varTag.nativeType in ConstantArrayTypeBuilder.php Closes https://github.com/phpstan/phpstan/issues/14305 --- phpstan-baseline.neon | 2 +- src/Type/Constant/ConstantArrayTypeBuilder.php | 17 +++++++++++++++++ .../Arrays/OffsetAccessAssignmentRuleTest.php | 6 ++++++ tests/PHPStan/Rules/Arrays/data/bug-14305.php | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/Arrays/data/bug-14305.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6c3ff7e1536..78d6e328a54 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -966,7 +966,7 @@ parameters: - rawMessage: PHPDoc tag @var with type float|int is not subtype of native type int. identifier: varTag.nativeType - count: 2 + count: 3 path: src/Type/Constant/ConstantArrayTypeBuilder.php - diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index c901306f289..aab3b8ad449 100644 --- a/src/Type/Constant/ConstantArrayTypeBuilder.php +++ b/src/Type/Constant/ConstantArrayTypeBuilder.php @@ -291,6 +291,23 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt $this->keyTypes[] = $scalarType; $this->valueTypes[] = $valueType; $this->optionalKeys[] = count($this->keyTypes) - 1; + + if (!($scalarType instanceof ConstantIntegerType)) { + continue; + } + + $max = max($this->nextAutoIndexes); + $offsetValue = $scalarType->getValue(); + if ($offsetValue < $max) { + continue; + } + + /** @var int|float $newAutoIndex */ + $newAutoIndex = $offsetValue + 1; + if (is_float($newAutoIndex)) { + $newAutoIndex = $max; + } + $this->nextAutoIndexes[] = $newAutoIndex; } $this->isList = TrinaryLogic::createNo(); diff --git a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php index 1c78cb3f23a..6b9af0d1124 100644 --- a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php @@ -153,6 +153,12 @@ public function testBug8015(): void $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-8015.php'], []); } + public function testBug14305(): void + { + $this->checkUnionTypes = false; + $this->analyse([__DIR__ . '/data/bug-14305.php'], []); + } + public function testBug11572(): void { $this->checkUnionTypes = true; diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14305.php b/tests/PHPStan/Rules/Arrays/data/bug-14305.php new file mode 100644 index 00000000000..1060a8d5703 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-14305.php @@ -0,0 +1,15 @@ + 0, + 'nupath' => 7, +]); + +$row = ['id' => 0]; + +foreach ([BUG14305_XDOC_GMETA_EMTY, BUG14305_XDOC_GMETA_NUPATH] as $meta) + $row[array_search($meta, BUG14305_XDOC_GMETAS)] = ''; From f9b8c830624c65ab317a12f2a1aa959f7a3e3414 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 16 Mar 2026 22:22:35 +0100 Subject: [PATCH 2/2] Use E2E test --- .github/workflows/e2e-tests.yml | 3 +++ e2e/bug-14305/phpstan.neon | 4 ++++ .../Arrays/data/bug-14305.php => e2e/bug-14305/test.php | 4 ++-- .../PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php | 6 ------ 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 e2e/bug-14305/phpstan.neon rename tests/PHPStan/Rules/Arrays/data/bug-14305.php => e2e/bug-14305/test.php (85%) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 167eeda27a8..4237d3f41ba 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -380,6 +380,9 @@ jobs: - script: | cd e2e/bug-11819 ../../bin/phpstan + - script: | + cd e2e/bug-14305 + ../../bin/phpstan - script: | cd e2e/composer-and-phpstan-version-config composer install --ignore-platform-reqs diff --git a/e2e/bug-14305/phpstan.neon b/e2e/bug-14305/phpstan.neon new file mode 100644 index 00000000000..5fbd64483ab --- /dev/null +++ b/e2e/bug-14305/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 5 + paths: + - test.php diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14305.php b/e2e/bug-14305/test.php similarity index 85% rename from tests/PHPStan/Rules/Arrays/data/bug-14305.php rename to e2e/bug-14305/test.php index 1060a8d5703..b7daf92cfe3 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-14305.php +++ b/e2e/bug-14305/test.php @@ -5,8 +5,8 @@ define('BUG14305_XDOC_GMETA_EMTY', 0); define('BUG14305_XDOC_GMETA_NUPATH', 7); define('BUG14305_XDOC_GMETAS', [ - 'empty' => 0, - 'nupath' => 7, + 'empty' => 0, + 'nupath' => 7, ]); $row = ['id' => 0]; diff --git a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php index 6b9af0d1124..1c78cb3f23a 100644 --- a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php @@ -153,12 +153,6 @@ public function testBug8015(): void $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-8015.php'], []); } - public function testBug14305(): void - { - $this->checkUnionTypes = false; - $this->analyse([__DIR__ . '/data/bug-14305.php'], []); - } - public function testBug11572(): void { $this->checkUnionTypes = true;