Skip to content

Fix #13217: Invalid second array value type in array_map#5099

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-q7783q8
Closed

Fix #13217: Invalid second array value type in array_map#5099
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-q7783q8

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When array_map is called with multiple arrays of different lengths, PHP pads the shorter arrays with null values. PHPStan was not accounting for this when determining callback parameter types, causing false positives like "Variable $bValue on left side of ?? always exists and is not nullable" when using the null coalescing operator on callback parameters that could actually be null.

Changes

  • src/Reflection/ParametersAcceptorSelector.php: When building the refined callable type for array_map's callback parameter, check if the array arguments have known constant sizes that differ. If so, add null to each callback parameter type using TypeCombinator::addNull().
  • src/Analyser/MutatingScope.php: Apply the same null-padding logic in getClosureType() when creating DummyParameter objects from arrayMapArgs for arrow functions and closure return type inference.
  • tests/PHPStan/Analyser/nsrt/bug-13217.php: New regression test covering different-length arrays, same-length arrays, unknown-length arrays, arrow functions, same-variable arrays, and single-array cases.

Root cause

PHPStan determines array_map callback parameter types in two places:

  1. ParametersAcceptorSelector (lines 87-132): Creates a refined callable type like callable(1|2, 3) based on the iterable value types of the array arguments. This determines the types used during the main closure body analysis.
  2. MutatingScope::getClosureType (lines 5707+): Creates DummyParameter objects from arrayMapArgs for computing closure/arrow function types.

Both places extracted getIterableValueType() from each array without considering that PHP's array_map pads shorter arrays with null. The fix adds a check: when all arrays have a single known constant size and those sizes differ, null is added to each callback parameter type.

The fix is conservative — it only adds null when array sizes are provably different (known constant sizes). For arrays with unknown sizes, it does not add null to avoid introducing false positives in generic code patterns like array_map($callback, $this->items, array_keys($this->items)).

Test

The regression test tests/PHPStan/Analyser/nsrt/bug-13217.php covers:

  • Different constant lengths: array_map(fn($a, $b) => ..., [1, 2], [3]) — parameters are nullable
  • Same constant lengths: array_map(fn($a, $b) => ..., [1, 2], [3, 4]) — parameters are NOT nullable
  • Unknown lengths: array_map(fn($a, $b) => ..., $a, $b) — parameters stay mixed
  • Arrow function with ??: The original reproducing case — $bValue ?? 1 no longer triggers false positive
  • Same variable: array_map(fn($x, $y) => ..., $a, $a) — parameters stay mixed
  • Single array: Standard single-array case is unaffected

Fixes phpstan/phpstan#13217

…stant sizes

- When array_map is called with multiple arrays of different known sizes,
  PHP pads shorter arrays with null, so callback parameters should be nullable
- Fixed ParametersAcceptorSelector to add null to callback parameter types
  when constant array sizes provably differ
- Fixed MutatingScope::getClosureType to apply the same logic for arrow
  functions and closure return type inference
- Added regression test in tests/PHPStan/Analyser/nsrt/bug-13217.php

Closes phpstan/phpstan#13217
@staabm staabm closed this Mar 1, 2026
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.

2 participants