Return the picked keys from array_rand() - #6223
Open
zonuexe wants to merge 2 commits into
Open
Conversation
zonuexe
force-pushed
the
fix/array-rand-return-type
branch
2 times, most recently
from
August 13, 2026 20:59
6d26322 to
a6c2c68
Compare
PHP casts a decimal-integer string array key ("123") to int, so
`array_key_first([$string => null])` is not necessarily a string. PHPStan
inferred `string` for it and reported `is_int()` on the result as always false.
UnsafeArrayStringKeyCastingTraverser already modelled that cast, but only for
the key type an array *has* — a type that also decides how the array describes
itself and what it accepts, which is why only
`reportUnsafeArrayStringKeyCasting: detect` widens it. Widening it with the
toggle off turns `array<string, X>` into `array<X>` everywhere.
castReadKeyType() is the second entry point, for a key that leaves the array as
a value of its own. With the toggle off it widens `string` to the benevolent
`(int|string)`, so neither branch reports anything; `detect` and `prevent` keep
the types they have today. unionWithReadKeyType() adds the `null` an empty array
gives back without losing the benevolence on the way.
The remaining accessors follow in the next commit.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
array_rand() collapsed the array's key type into int, string or int|string, and
returned `array<int, key>` whenever $num was more than one. Hand back the key
type itself, and build a tuple when $num is a known constant:
array_rand(['a' => 1, 'b' => 2]) 'a'|'b' (was string)
array_rand($list) int<0, max> (was int)
array_rand($shape, 2) array{key, key} (was array<int, key>)
array_rand($shape, $atLeastTwo) non-empty-list<key>
An array comes back only when $num is at least 2, since a single pick returns
the key on its own, so the list is non-empty in every branch that produces one.
KEY_COUNT_LIMIT caps the tuple at 100 elements, past which
ConstantArrayTypeBuilder would degrade the shape anyway.
The keys run through castReadKeyType(), so `array<string, X>` gives
`(int|string)` rather than a certain `string`.
array_rand([]) returns never now, which is what PHP 8 does (ValueError). That
also makes everything after the call unreachable, so the two calls in
data/array_rand.php moved into separate functions to keep their diagnostics.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
zonuexe
force-pushed
the
fix/array-rand-return-type
branch
from
August 14, 2026 04:10
a6c2c68 to
7601b6a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #6222. Its first commit is cherry-picked here so this branch analyses on its own; I will rebase it away once it lands.
Problem
array_rand()threw away what it knew about the keys. It collapsed the key type intoint,stringorint|string, and returnedarray<int, key>whenever$numwas more than one:Change
Hand back the key type itself, and build a tuple when
$numis a known constant:array_rand($shape)string'a'|'b'|'c'array_rand($list)intint<0, max>array_rand($shape, 2)array<int, string>array{'a'|'b'|'c', 'a'|'b'|'c'}array_rand($shape, $atLeastTwo)array<int, string>non-empty-list<'a'|'b'|'c'>array_rand($shape, $positive)array<int, string>|string'a'|'b'|'c'|non-empty-list<'a'|'b'|'c'>An array comes back only when
$numis at least 2, since a single pick returns the key on its own. That makes the list non-empty in every branch that produces one.KEY_COUNT_LIMITcaps the tuple at 100 elements. Past thatConstantArrayTypeBuilderwould degrade the shape anyway.The keys run through
castReadKeyType()from #6222, soarray<string, X>gives(int|string)rather than a certainstring.array_rand([]) returns never now
array_rand([])throws a ValueError on PHP 8, soneveris the honest type. It also makes everything after the call unreachable, which cost the second call indata/array_rand.phpits diagnostics. The two calls sit in separate functions now.Tests
nsrt/array-rand.phpis new.nsrt/array-functions.phpanddata/bug-9803.phpgain precision, and two rule tests carry the new types in their messages.