Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[EventDispatcher] Allow EventDispatcher to dispatch anonymous events #21962

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

Closed
wants to merge 1 commit into from
Closed

[EventDispatcher] Allow EventDispatcher to dispatch anonymous events #21962

wants to merge 1 commit into from

Conversation

santakadev
Copy link

@santakadev santakadev commented Mar 10, 2017

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets n/a
License MIT
Doc PR pending
  • submit changes to the documentation

Feture description

This PR allows EventDispatcher to dispatch anonymous events. Internally the fully qualified event object class name is used.

To add a listener or subscribe to an anonymous event, simply use the event object fully qualified name:

// EventSubscriber

public function getSubscribedEvents()
{
    return [CustomEvent::class => 'onCustomEvent']
}
// EventListener

$dispatcher->addListener(CustomEvent::class, $listener);

To dispatch an anonymous event:

class CustomEvent
{
    // ...
}

$dispatcher->dispatchAnonymousEvent(new CustomEvent());

To stop event propagation:

$dispatcher->stopPropagation($event);

I have removed the coupling to Event class for these reasons:

  • To remove client code coupling to this component.
  • To allow clients to design immutable events.
  • To move the stop propagation responsability to another object.

ORIGINAL PULL REQUEST - Allow EventDispatcher to dispatch any object

Feture description

This PR allows EventDispatcher to dispatch any object. This way, clients of this component are not forced to couple to Event class.

I have deprecated Event class, because it would not be the base class for all events anymore. Also, I have created a clone of this class named StoppableEvent. This would be the new class which clients should use in order to make "stop propagation" feature available for that event.

Motivation

  • In my opinion, the stop propagation feature is not always desirable.
  • From de DDD/EventSourcing prespective, in order to use this component, you are forced to couple your Domain Events to the Event class. This can be fixed by creating a Wrapper object, but then you loose the type hinting in the listeners.

{
if (null === $event) {
$event = new Event();
}

if (!is_object($event)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why even forcing an object as a type?

@unkind
Copy link
Contributor

unkind commented Mar 10, 2017

In my opinion, the stop propagation feature is not always desirable.
This can be fixed by creating a Wrapper object, but then you loose the type hinting in the listeners.

There was similar ticket few days ago: #21827
You can solve it by overriding doDispatch method: #18873

As I said in previous ticket, the Symfony EventDispatcher has its own philosophy, if it doesn't fit your needs, then it's better to change the tool.

@santakadev
Copy link
Author

My intention was to maintain the same philosophy, but allowing us to dispatch other objets.

I think that It would make the component more attractive to be used as standalone.

I'm currently familiar with other tools, I was just trying to help.

Thanks for your time and links!

@unkind
Copy link
Contributor

unkind commented Mar 11, 2017

This won't be merged anyway, because changing interface signature is a BC break.

However, it might be possible to raise the question about EventListenerCaller from #18873 again: it looks like I'm not the only one who faced with this issue.

@nicolas-grekas nicolas-grekas added this to the 3.x milestone Mar 14, 2017
@nicolas-grekas
Copy link
Member

Dunno if another approach is possible, but as @unkind explained, this is a BC break, so no go.

@ostrolucky
Copy link
Contributor

Who says this have to be merged in 3.x. What's the procedure for submitting BC break features? In this case would we have to create a copy of EventDispatcher class, implement new interface and deprecate the old one?

@Nek-
Copy link
Contributor

Nek- commented Mar 18, 2017

@gadelat I guess the implementation should be reworked to make it work with new methods/interfaces (and deprecate the old).

@santakadev santakadev changed the title [EventDispatcher] Allow EventDispatcher to dispatch any object [EventDispatcher] Allow EventDispatcher to dispatch anonymoys events Mar 19, 2017
- Added AnonymousEventDispatcherInterface to allow event dispatching without the need of specifying a name
- Added EventPropagationTrackerInterface to remove the need of anonymous events to be coupled to Event class
- Implemented new interfaces in EventDispatcher
@santakadev
Copy link
Author

Thank you all for your comments.

To achieve the goals I have worked in another solution. For more details look at the updated PR description.

@santakadev santakadev changed the title [EventDispatcher] Allow EventDispatcher to dispatch anonymoys events [EventDispatcher] Allow EventDispatcher to dispatch anonymous events Mar 19, 2017
public function stopPropagation($event)
{
if (!$this->isPropagationStopped($event)) {
$this->stoppedEvents[] = $event;
Copy link
Member

Choose a reason for hiding this comment

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

making the EventDispatcher stateful is a nogo (this is leaking memory by keeping a reference to the object btw)

*/
public function isPropagationStopped($event)
{
return in_array($event, $this->stoppedEvents);
Copy link
Member

Choose a reason for hiding this comment

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

comparing the whole object graph for equality is a nightmare for performance

*
* @param object $event The event to pass to the event handlers/listeners
*/
public function dispatchAnonymousEvent($event);
Copy link
Member

Choose a reason for hiding this comment

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

why removing the event name ? This limits the use cases, as it forces creating a new class for each place dispatching an event as the event class is used as event identifier.

@stof
Copy link
Member

stof commented Mar 30, 2017

I'm -1 for the removal of event names. This would make the dispatcher unusable for Symfony itself in several cases (all form events are using the same FormEvent class for the object for instance).

And the way stopping the propagation is reimplemented is worse than currently:

  • leaks memory
  • makes the dispatcher stateful
  • has 0(n) complexity based on the number of stopped events for a call done on each listener call, while the current code is O(1)
  • does not make the stopping feature optional, as it know depends whether the dispatcher itself implements the interface, which is either always or never the case (and btw, decorating dispatchers are not implementing the feature, which makes the feature totally unusable).

@santakadev santakadev closed this Mar 30, 2017
@ostrolucky
Copy link
Contributor

If someone stumbles here and find he needs same thing, I've created separate event dispatcher library which doesn't restrict you in this way and is more appropriate for use cases like these https://github.com/ostrolucky/app-event-dispatcher

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

Successfully merging this pull request may close these issues.

8 participants