Skip to content

Add null to array_map() callback parameters of shorter arrays - #6874

Open
SanderMuller wants to merge 1 commit into
phpstan:2.3.xfrom
SanderMuller:array-map-null-padding
Open

SanderMuller wants to merge 1 commit into
phpstan:2.3.xfrom
SanderMuller:array-map-null-padding

Conversation

@SanderMuller

Copy link
Copy Markdown
Contributor

With more than one array, array_map() calls the callback once per index of the longest array. Each shorter array gives null for the missing indexes:

php -r 'var_dump(array_map(fn ($a, $b) => [$a, $b], [1], [3, 4]));'

The second call gets $a = null, so the first array is padded too, not only the 2nd+ arrays.

PHPStan typed each callback parameter as the value type of its array, without null. This PR adds null to the parameter of an array whose largest size is smaller than the largest size of another array. It does this only when the size of every array is known, for example constant arrays or array{1, 2}|array{1}.

It compares the largest sizes and not the smallest, because array_map($cb, $a, $a) never pads, whatever sizes $a can have. An arg with ... is skipped where it is not expanded into its arrays.

The logic is in ArrayMapCallbackParameterTypes. Four places use it: ParametersAcceptorSelector, ContextualClosureParameterResolver, ArrayMapFunctionReturnTypeExtension and ArrayMapParameterClosureTypeExtension. The two shadowed classes call the PHP helper from their C++ twins.

A typed callback such as function (int $a, int $b) over [1, 2] and [3] now gets an argument error. PHP throws a TypeError for that call.

General arrays stay as they are. I tried to make them nullable too, but three existing regression tests then report errors:

  • array_map($cb, $arr, $arr) (bug-12317)
  • array_map($callback, $this->items, array_keys($this->items)) (array_map_multiple.php)
  • array_map(fn (string $a, string $b) => ..., ...$tuple) with array{list<string>, list<string>} (bug-10527)

The first is provably the same length, the second is the same length in practice, and the third can differ. Should general arrays become nullable, maybe in bleeding edge, with an exception for the same expression?

Closes phpstan/phpstan#13217

🤖 Generated with Claude Code

With more than one array, array_map() passes null for the missing
indexes of each shorter array. When the size of every array is known,
the callback parameter of an array that can be shorter than another
one now gets null.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid second array value type in array_map

1 participant