Skip to content

Unpacked args to array_map() and immediately invoked closures are typed as one value #15620

Open

Description

@SanderMuller

Bug report

An unpacked arg (...$x) is typed as one value. PHP expands it into one value per array item. This gives wrong closure parameter types for array_map() and for immediately invoked closures, a wrong array_map() return type, and a false argument.type.

The cause is in three places:

  • ContextualClosureParameterResolver::resolve() makes one parameter for each arg, also for an unpacked arg.
  • ArrayMapFunctionReturnTypeExtension takes an unpacked arg as one array.
  • In ParametersAcceptorSelector::applyIntrinsicArgOverrides(), an unpacked arg that is not a constant array adds no parameter, so the callback type becomes callable().

When the number of arrays is not known, for example with ...$lists, the types after that arg are only known as a rest type. Then the callback check should not report an error.

#10527 is related, but it was about the return type only. This came up in a review on phpstan/phpstan-src#6874. I have a fix ready and will open a PR after phpstan/phpstan-src#6874 is merged, because both change the same code.

Code snippet that reproduces the problem

<?php declare(strict_types = 1);

/**
 * @param list<list<int>> $lists
 */
function doFoo(array $lists): void
{
	array_map(function ($a, $b) {
		\PHPStan\dumpType($b);
	}, [1, 2, 3], ...[[4, 5, 6]]);

	(function ($a, $b) {
		\PHPStan\dumpType($a);
	})(...[1, 2]);

	\PHPStan\dumpType(array_map(fn ($a, $b) => $b, [1, 2, 3], ...[[4, 5, 6]]));

	array_map(fn (int $a, int $b) => $a + $b, ...$lists);
}

Expected output

On PHPStan 2.3.4, level 8:

Line Actual Expected
9 array{4, 5, 6} 4|5|6
13 array{1, 2} 1
16 non-empty-list<array{4, 5, 6}> non-empty-list<4|5|6>
18 Parameter #1 $callback of function array_map expects (callable(): mixed)|null, Closure(int, int): int given. no error

At runtime, $b gets 4, 5 and 6, $a gets 1, and the array_map() result is [4,5,6].

Did PHPStan help you today? Did it make you happy in any way?

No response

Activity

  1. mergeable commented on Oct 31, 2026

    @mergeable

    This bug report is missing a link to reproduction at phpstan.org/try.

    It will most likely be closed after manual review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions