8000 [EventDispatcher] Allow to omit the event name when registering listeners by derrabus · Pull Request #33851 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[EventDispatcher] Allow to omit the event name when registering listeners #33851

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

Merged
merged 1 commit into from
Oct 9, 2019

Conversation

derrabus
Copy link
Member
@derrabus derrabus commented Oct 4, 2019
Q A
Branch? 4.4
Bug fix? no
New feature? yes
Deprecations? no
Tickets #33453 (kind of)
License MIT
Doc PR TODO

After #30801 and #33485, this is another attempt at taking advantage of FQCN events for simplifying the registration of event listeners by inferring the event name from the parameter type declaration of the listener. This is my last attempt, I promise. 🙈

This time, I'd like to make the event attribute of the kernel.event_listener tag optional. This would allow us to build listeners like the following one without adding any attributes to the kernel.event_listener tag.

namespace App\EventListener;

final class MyRequestListener
{
    public function __invoke(RequestEvent $event): void
    {
        // do something
    }
}

This in turn allows us to register a whole namespace of such listeners without having to configure each listener individually:

services:
    App\EventListener\:
        resource: ../src/EventListener/*
        tags: [kernel.event_listener]

@derrabus
Copy link
Member Author
derrabus commented Oct 4, 2019

cc @yceruto who has worked on the previous PR.

@derrabus derrabus force-pushed the feature/omit-event-name branch 2 times, most recently from 49a313c to 58b316d Compare October 4, 2019 15:57
@nicolas-grekas nicolas-grekas added this to the next milestone Oct 4, 2019
@derrabus derrabus force-pushed the feature/omit-event-name branch from 58b316d to f6b6966 Compare October 4, 2019 19:30
@nicolas-grekas
Copy link
Member
nicolas-grekas commented Oct 4, 2019

My 2cts:

when and only when the "event" attribute is missing:

  • look also for public static getDe 8000 faultListenerPriority(): int
  • skip the listener when it also has the event_subscriber tag
  • refuse running the added logic if count($events) > 1

@derrabus
Copy link
Member Author
derrabus commented Oct 6, 2019
  • look also for public static getDefaultListenerPriority(): int

Fine by me, although personally I'd probably use the attribute or a subscriber if I wanted to change the priority.

services:
    App\EventListener\:
        resource: ../src/EventListener/*
        tags: [kernel.event_listener]

    App\EventListener\VeryImportantListener:
        tags: [{name: kernel.event_listener, priority: 1337}]
  • skip the listener when it also has the event_subscriber tag

Makes sense, but I'd prefer to throw here. The developer should know that we don't want to process a piece of configuration they gave us.

  • refuse running the added logic if count($events) > 1

That would render the following configuration invalid, that would work with my current implementation.

services:
    App\EventListener\MyListener:
        tags:
            - {name: kernel.event_listener, method: onFoo}
            - {name: kernel.event_listener, method: onBar}

What would be the reason for that limitation?

@nicolas-grekas
Copy link
Member

skip the listener when it also has the event_subscriber tag

I insist on this one as that allows configuring a directory with the tag, defaulting to listeners, but allowing to augment them to subscribers, all that without touching the configuration files.

Fine for the rest.

@derrabus derrabus force-pushed the feature/omit-event-name branch from f6b6966 to da25786 Compare October 6, 2019 14:07
@derrabus
Copy link
Member Author
derrabus commented Oct 6, 2019

that allows configuring a directory with the tag, defaulting to listeners, but allowing to augment them to subscribers, all that without touching the configuration files.

Fair point! kernel.event_listener without event attribute is now skipped if kernel.event_subscriber is also present.

Fine for the rest.

All right then. We can discuss/implement the functionality around getDefaultListenerPriority() in a separate PR if we really want that. This PR works well without it, I think.

Copy link
Member
@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

(with minor wording suggestion)

@derrabus derrabus force-pushed the feature/omit-event-name branch from da25786 to 6f32584 Compare October 7, 2019 13:29
@nicolas-grekas
Copy link
Member

Thank you @derrabus.

nicolas-grekas added a commit that referenced this pull request Oct 9, 2019
…gistering listeners (derrabus)

This PR was merged into the 4.4 branch.

Discussion
----------

[EventDispatcher] Allow to omit the event name when registering listeners

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | #33453 (kind of)
| License       | MIT
| Doc PR        | TODO

After #30801 and #33485, this is another attempt at taking advantage of FQCN events for simplifying the registration of event listeners by inferring the event name from the parameter type declaration of the listener. This is my last attempt, I promise. 🙈

This time, I'd like to make the `event` attribute of the `kernel.event_listener` tag optional. This would allow us to build listeners like the following one without adding any attributes to the `kernel.event_listener` tag.

```php
namespace App\EventListener;

final class MyRequestListener
{
    public function __invoke(RequestEvent $event): void
    {
        // do something
    }
}
```

This in turn allows us to register a whole namespace of such listeners without having to configure each listener individually:

```YAML
services:
    App\EventListener\:
        resource: ../src/EventListener/*
        tags: [kernel.event_listener]
```

Commits
-------

6f32584 [EventDispatcher] Allow to omit the event name when registering listeners.
@nicolas-grekas nicolas-grekas merged commit 6f32584 into symfony:4.4 Oct 9, 2019
@zmitic
Copy link
zmitic commented Oct 13, 2019

Can it be autowired with some EventListenerInterface? And if priority can be set via another interface so final result would be

class MyListener implements EventListenerInterface, PrioritizedEventInterface
{
    public function __invove(SomeEvent $event) {}

    public static function getPriority()
    {
        return 10;
    }
}

This way we avoid unused method error and can ctrl+click to see all listeners.

@derrabus derrabus deleted the feature/omit-event-name branch October 13, 2019 12:20
@derrabus
Copy link
Member Author

Can it be autowired with some EventListenerInterface?

Listeners can be autowired and they don't require any interface for this. What you probably meant is autoconfiguration: The kernel.event_listener tag should be added if a certain interface is implemented by the listener class.

First of all, we cannot create an interface that includes the __invoke() method in a helpful way because each implementation needs to change the parameter type declaration. That interface would thus just be an empty marker.

Secondly, my intention was to keep this feature as simple as possible because we already have event subscribers for more complex setups. Subscribers offer that kind of autoconfiguration and they also allow you to specify a priority.

What you can do in your application code is create an EventListenerInterface without any methods and create an auto-configuration that just adds the tag.

This way we avoid unused method error

No, it wouldn't (because we cannot add __invoke() to the interface). Also, I'm not sure if Symfony should work around false-positive PhpStorm inspections.

Related: Haehnchen/idea-php-symfony2-plugin#1366

and can ctrl+click to see all listeners.

No, you would only see the the listeners that implement the marker interface. The charm of event listeners (compared to subscribers) is that they don't require any interface.

@zmitic
Copy link
zmitic commented Oct 13, 2019

What you probably meant is autoconfiguration: The kernel.event_listener tag should be added if a certain interface is implemented by the listener class.

You are right, my bad. I already (ab)use it in Kernel like this and still confuse names in a rush:

$container->registerForAutoconfiguration(MyInterface::class)->addTag(MyInterface::class);

No, it wouldn't (because we cannot add __invoke() to the interface)

Sorry, I meant unused method getDefaultListenerPriority, not the __invoke.

No, you would only see the the listeners that implement the marker interface.

Exactly. We already have marker interfaces: https://github.com/symfony/messenger/blob/master/Handler/MessageHandlerInterface.php

I think event listeners should do the same.

Anyway I can always create my own like I already do but was hoping for standardized version.

@nicolas-grekas nicolas-grekas modified the milestones: next, 4.4 Oct 27, 2019
This was referenced Nov 12, 2019
@reanim8ed
Copy link
reanim8ed commented May 16, 2020

Registered listeners with:

services:
    App\EventListener\:
        resource: ../src/EventListener/*
        tags: [kernel.event_listener]

Then tried to use listener for security.interactive_login event:

  • It works using __invoke method
  • As I understand then the kernel.event_listener tag doesn't define event, the method whose name is on + CamelCased event name should be executed. Tried with onSecurityInteractiveLogin - it doesnt work this way.

Using SF 4.4.5

@yceruto
Copy link
Member
yceruto commented May 16, 2020

@reanim8ed you've probably spotted a bug, but can you create a new issue to discuss about it?

https://github.com/symfony/symfony/issues/new?labels=Bug&template=1_Bug_report.md

@reanim8ed
Copy link

Sure, created here: 36840

@derrabus
Copy link
Member Author
derrabus commented May 16, 2020

As I understand then the kernel.event_listener tag doesn't define event, the method whose name is on + CamelCased event name should be executed. Tried with onSecurityInteractiveLogin - it doesnt work this way.

No, if you don't specify the event, only __invoke() will work. The compiler pass is not browsing all on* methods on that class.

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.

6 participants
0