Skip to content

Conversation

@michaelruelas
Copy link
Contributor

@michaelruelas michaelruelas commented Jan 7, 2026

Closes #119

Description

This pull request addresses an issue where SerializableClosure would incorrectly serialize the first closure found on a line when multiple closures were defined on that same line (e.g., arrow functions in an array or passed as arguments).

Previously, the ReflectionClosure::getCode() logic would greedily return the first fn or function definition it encountered on the source line. This PR introduces a robust candidate selection mechanism:

  1. It collects all possible closure definitions found on the matching lines.
  2. It validates each candidate against the actual ReflectionFunction properties (parent class of ReflectionClosure) such as:
    • Static status (static fn vs fn).
    • Parameter count.
    • Captured variables (uses a sub-tokenization pass on the candidate code to ensure local variables and captured variables match the reflection data).
  3. It selects the first candidate that matches all these criteria.

Benefit to End Users

This fix allows users to more reliably use arrow functions with Laravel's concurrency and background processing features (like Concurrency::run()), where short closures are often defined inline on a single line. It removes a subtle data corruption bug where the wrong closure logic would be executed after deserialization.

Why it doesn't break existing features

The implementation purely adds disambiguation logic to the code extraction phase. It follows existing parser patterns and adds a secondary validation step using official Reflection metadata. Existing unit tests (331 total) all pass, and the new disambiguation logic only triggers when the reflection data confirms a match.

Tests

New tests have been added in tests/MultipleClosuresOnSameLineTest.php covering scenarios with different arguments, different static variables, and static/non-static mixtures.

@taylorotwell taylorotwell merged commit 48328ed into laravel:2.x Jan 8, 2026
11 of 12 checks passed
@michaelruelas michaelruelas deleted the fix-same-line-closures branch January 8, 2026 17:03
@Niush
Copy link

Niush commented Jan 8, 2026

Hi @michaelruelas,
I had opened the original issue, and just tested it locally.

These work nicely:

$a = "a";
$b = "b";
$correct = Concurrency::driver('process')->run([fn () => $a, fn() => $b]);
$correct = Concurrency::driver('process')->run([fn () => "a", static fn() => "b"]);

// ["a", "b"]

But these are still not working:

$incorrect = Concurrency::driver('process')->run([fn () => "a", fn() => "b"]);
$incorrect = Concurrency::driver('process')->run([function () { return "a"; }, function () { return "b"; }]);
$incorrect = Concurrency::driver('process')->run([fn () => "a", function () { return "b"; }]);

// ["a", "a"]

Is this a limitation of this PR?

By the way, if I make one normal fn() and another static fn() it does work.

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.

Concurrency, Arrow Function and SerializableClosure Issues

3 participants