Skip to content

[13.x] Resolve scheduled event callback parameter by type rather than name#60197

Merged
taylorotwell merged 2 commits into
laravel:13.xfrom
kayw-geek:fix/scheduled-event-callback-type-based-injection
May 20, 2026
Merged

[13.x] Resolve scheduled event callback parameter by type rather than name#60197
taylorotwell merged 2 commits into
laravel:13.xfrom
kayw-geek:fix/scheduled-event-callback-type-based-injection

Conversation

@kayw-geek
Copy link
Copy Markdown
Contributor

Fixes a name-based parameter resolution issue introduced in #60144.

Background

#60144 made it possible to pass the scheduled Event into lifecycle callbacks, and the PR description promises that you can "typehint a callback with Event $event as a parameter". However, the implementation only injects the event when the closure parameter is literally named $event. The is_a($this, $eventParameterType) check in the same method shows the intent was type-based, but the lookup is name-based — these contradict each other.

All tests added by #60144 happen to use the parameter name $event, so the working contract (typehint-based) was never exercised.

Reproduction

Schedule::command('inspire')
    ->before(function (Event $scheduledEvent) {
        Log::info("Starting {$scheduledEvent->command}");
    });

This throws BindingResolutionException: the parameter lookup misses on the name 'event', no value is injected, and the container falls back to autowiring a new Event — which fails because EventMutex isn't instantiable.

Fix

Iterate the closure parameters and inject $this into the first parameter whose type is a class that $this is an instance of. This matches how Laravel container DI works elsewhere — by type, not by name.

After

Schedule::command('inspire')
    ->before(function (Event $scheduledEvent) {  // works
        Log::info("Starting {$scheduledEvent->command}");
    });

Schedule::command('inspire')
    ->before(function (Event $event) {  // still works
        Log::info("Starting {$event->command}");
    });

Comment on lines +794 to +800
foreach ($parameters as $name => $type) {
if ($type !== null && is_a($this, $type)) {
return [$name => $this];
}
}

return ['event' => $this];
return [];

This comment was marked as low quality.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

array_any returns bool though, so $event would just be true/false — $name from the closure isn't in scope either. Did you mean array_find_key?

Either way I'd lean toward keeping the foreach — the other 3 spots using closureParameterTypes() in this file all do the same $parameters = ...; foreach thing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry–my bad 🤦🏻 😅

@taylorotwell taylorotwell merged commit c16fe0c into laravel:13.x May 20, 2026
52 checks passed
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.

3 participants