From f9a19f8568fc701c9af1f041ad5c53b5745cda38 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 20 May 2026 16:07:29 +0200 Subject: [PATCH 1/3] Add array assertions --- tests/PHPStan/Analyser/nsrt/array-fill-keys.php | 10 ++++++++++ tests/PHPStan/Analyser/nsrt/array-flip.php | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/array-fill-keys.php b/tests/PHPStan/Analyser/nsrt/array-fill-keys.php index 39d1df4b5da..60fc63d873b 100644 --- a/tests/PHPStan/Analyser/nsrt/array-fill-keys.php +++ b/tests/PHPStan/Analyser/nsrt/array-fill-keys.php @@ -104,3 +104,13 @@ function withNotConstantArray(array $foo, array $bar, array $baz, array $floats, assertType('non-empty-array', array_fill_keys($mixed, null)); } } + +/** + * @param array{0: 1, 1: 2, 2: 3} $sealed + * @param array{0: 1, 1: 2, 2: 3, ...} $unsealed + */ +function sealedArrayFillKeys(array $sealed, array $unsealed): void +{ + assertType("array{1: 'b', 2: 'b', 3: 'b'}", array_fill_keys($sealed, 'b')); + assertType("array{1: 'b', 2: 'b', 3: 'b', ...<0|1, 'b'>}", array_fill_keys($unsealed, 'b')); +} diff --git a/tests/PHPStan/Analyser/nsrt/array-flip.php b/tests/PHPStan/Analyser/nsrt/array-flip.php index 9ec89f5c1d0..6198d7a6301 100644 --- a/tests/PHPStan/Analyser/nsrt/array-flip.php +++ b/tests/PHPStan/Analyser/nsrt/array-flip.php @@ -93,3 +93,14 @@ function foo10(array $array) assertType("*NEVER*", array_flip($array)); // this could be array&hasOffsetValue(17, 'bar') according to https://3v4l.org/1TAFk } } + +/** + * @param array{0: 1, 1: 2, 2: 3} $sealed + * @param array{0: 1, 1: 2, 2: 3, ...} $unsealed + */ +function sealedArrayFlip(array $sealed, array $unsealed): void +{ + assertType('array{1: 0, 2: 1, 3: 2}', array_flip($sealed)); + assertType('array{1: int, 2: 1, 3: 2, 0?: int, ...<0|1, int>}', array_flip($unsealed)); + assertType('int', array_flip($unsealed)[1]); +} From 22deaa68a4536e83013443c75a4af9dd6ab3b71c Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 20 May 2026 16:13:59 +0200 Subject: [PATCH 2/3] Add fix --- src/Type/Constant/ConstantArrayType.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 5a22f5b189a..865eb264f9a 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -1522,6 +1522,8 @@ public function flipArray(): Type if ($this->isUnsealed()->yes() && $this->unsealed !== null) { [$unsealedKey, $unsealedValue] = $this->unsealed; $builder->makeUnsealed($unsealedValue->toArrayKey(), $unsealedKey); + + $builder->setOffsetValueType($unsealedValue->toArrayKey(), $unsealedKey, true); } return $builder->getArray(); From 6a5752ff388165170688e85e9aac434a4d216719 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 20 May 2026 16:18:18 +0200 Subject: [PATCH 3/3] Update tests --- tests/PHPStan/Analyser/nsrt/unsealed-derivations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/nsrt/unsealed-derivations.php b/tests/PHPStan/Analyser/nsrt/unsealed-derivations.php index 6b85cae6bcf..1766ba6e54b 100644 --- a/tests/PHPStan/Analyser/nsrt/unsealed-derivations.php +++ b/tests/PHPStan/Analyser/nsrt/unsealed-derivations.php @@ -292,7 +292,7 @@ public function flipPreservesUnsealed(array $arr): void // `array_flip` swaps keys and values pair-by-pair, so the // unsealed `` becomes `` — the value // type passes through `toArrayKey()` to land in the new key slot. - assertType("array{foo: 'a', bar: 'b', ...}", array_flip($arr)); + assertType("array{foo: 'a'|int, bar: 'b'|int, ...}", array_flip($arr)); } /**