-
Notifications
You must be signed in to change notification settings - Fork 47
fix: Phase::Handle event hooks cause side-effects to original Event::Handle return value #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
ℹ️ UpdateIt looks like the root cause is an interplay between |
ℹ️ Extented existing
|
538df89 to
b6324d9
Compare
b6324d9 to
84cd4c6
Compare
…Handle return value
84cd4c6 to
02b3b21
Compare
|
I don't see this as a bug… calling |
Thanks for the reply! And indeed, "bug" might be the wrong classification. But with the premise of the event handle return not being "special", the commit return value would effectively be a wildcard that you cannot "trust" unless you have e.g. test coverage for your use case of the return value. So, while not bug, I'd claim that if there is no guarantee/defined behavior, the utility of this return behavior would be affected. Because there no longer would be a contract that you can rely on. Or to turn it around: what would be the benefit of offering the (current) flexibility of being able to hook into the return value and alter what is being returned? Is there a use case that I'm missing where the ability to affect the data and shape of the return value would bring value? For me, the wonderful self-contained-ness of the verbs event classes suffers a bit of a hit if one loses control of what the event "intends" to return by the mere existence of a potentially rogue event listener. It feels a bit like mutating an assumed-to-be private property. TL;DR: Is there a benefit of providing the flexibility for event listeners to add arbitrary return values vs. the guarantee that you can safely use the return value of your handle method? Looking at the code, I understand the idea of not treating the "original" handle method any different - it's all just registered hooks. But factually, a differentiation seems like a benefit to me that provides extra guarantees when working with my events. I guess I could simply always only consider the first return value, but I would at least need to either take the singular value or the first element of a collection, in case some listener got it's hand on my event. PS: Please don't see this "rant" as a critique or complaining :) |
|
Quick update for anyone facing this or interested in a safe(er) way to tackle this. In general, I would suggest not relying on the return value of your If you still want to use the feature, you should take control over the return value as much as possible by handling the possibility that a collection will be returned. Below are two example approaches, with some limitations.
If anybody has ideas for a more robust solution, I'm all ears :) A) Per-case basisWhen you want to obtain the $value = Collection::wrap(MyEvent::commit())->first();B) Event Base ClassFor a more generalized solution, one could introduce a base class for events. Note that I would probably not override the base <?php
namespace App\Events;
use Illuminate\Support\Collection;
use Thunk\Verbs\Event;
class VerbsBaseEvent extends Event
{
/** @see https://github.com/hirethunk/verbs/pull/218 */
public static function commitAndExclusivelyReturnOriginalHandleReturnValue(...$args): mixed
{
return Collection::wrap(static::commit(...$args))->first();
}
} |
ℹ️ Follow-up: Verbs DocsApologies for being pedantic, but it would probably be worth extending the Verbs docs with some clarifications. Here a screenshot from the current docs, with some snarky comments 😅
|

This PR is a proposal to resolve #213.
Symptom:
Listeners for the
Phase::Handlecause an event'shandlereturn value to be altered.Root cause:
The Dispatcher::handle methods returns all return values by registered lifecycle hooks. This includes the original event's
handlereturn value as well as the return values of any listeners onPhase::Handle.Proposed solution:
Ensure that
Dispatcher::handlewill only consider the original event's return value.