8000 feature #33793 [EventDispatcher] A compiler pass for aliased userland… · symfony/symfony@8f92594 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8f92594

Browse files
feature #33793 [EventDispatcher] A compiler pass for aliased userland events (derrabus)
This PR was merged into the 4.4 branch. Discussion ---------- [EventDispatcher] A compiler pass for aliased userland events | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | TODO Since 4.3, the EventDispatcher component allows to register events via the FQCN of the class name instead of a dedicated event name. Earlier this year I have worked with a team that used the event dispatcher for own custom events. When 4.3 was released, the team decided to use the new mechanism for new events. For the sake of consistency, we also wanted to migrate existing event subscribers to FQCN events. While FrameworkBundle implements a nice aliasing mechanism for its own events, we couldn't find an obvious way to make use of FQCN event aliases for our own events. The best way we could find is registering a compiler pass that would extend an internal parameter that stores all event aliases. But that made us feel like we're fiddling with an implementation detail of the framework. This PR aims to provide a standard way for applications and third-party bundles to register their own event aliases. ```php $container->addCompilerPass(new EventAliasesPass([ MyCustomEvent::class => 'my_custom_event', ])); ``` Furthermore, it adds tests for class aliasing to the component's test suite. Additionally, the newly introduced pass is dogfooded by the SecurityBundle, so FrameworkBundle doesn't need to know about events fired by the security components. Commits ------- 34efe40 [EventDispatcher] A compiler pass for aliased userland events.
2 parents 496346c + 34efe40 commit 8f92594

File tree

13 files changed

+244
-5
lines changed

13 files changed

+244
-5
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323
<parameter key="Symfony\Component\HttpKernel\Event\ViewEvent">kernel.view</parameter>
2424
<parameter key="Symfony\Component\HttpKernel\Event\ExceptionEvent">kernel.exception</parameter>
2525
<parameter key="Symfony\Component\HttpKernel\Event\TerminateEvent">kernel.terminate</parameter>
26-
<parameter key="Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent">security.authentication.success</parameter>
27-
<parameter key="Symfony\Component\Security\Core\Event\AuthenticationFailureEvent">security.authentication.failure</parameter>
28-
<parameter key="Symfony\Component\Security\Http\Event\InteractiveLoginEvent">security.interactive_login</parameter>
29-
<parameter key="Symfony\Component\Security\Http\Event\SwitchUserEvent">security.switch_user</parameter>
3026
<parameter key="Symfony\Component\Workflow\Event\GuardEvent">workflow.guard</parameter>
3127
<parameter key="Symfony\Component\Workflow\Event\LeaveEvent">workflow.leave</parameter>
3228
<parameter key="Symfony\Component\Workflow\Event\TransitionEvent">workflow.transition</parameter>

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"symfony/messenger": "<4.3",
7979
"symfony/mime": "<4.4",
8080
"symfony/property-info": "<3.4",
81+
"symfony/security-bundle": "<4.4",
8182
"symfony/serializer": "<4.2",
8283
"symfony/stopwatch": "<3.4",
8384
"symfony/translation": "<4.4",

src/Symfony/Bundle/SecurityBundle/SecurityBundle.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@
3333
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory;
3434
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
3535
use Symfony\Component\DependencyInjection\ContainerBuilder;
36+
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
3637
use Symfony\Component\HttpKernel\Bundle\Bundle;
38+
use Symfony\Component\Security\Core\AuthenticationEvents;
39+
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
40+
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
41+
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
42+
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
43+
use Symfony\Component\Security\Http\SecurityEvents;
3744

3845
/**
3946
* Bundle.
@@ -68,5 +75,12 @@ public function build(ContainerBuilder $container)
6875
$container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_BEFORE_REMOVING);
6976
$container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
7077
$container->addCompilerPass(new RegisterTokenUsageTrackingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 200);
78+
79+
$container->addCompilerPass(new AddEventAliasesPass([
80+
AuthenticationSuccessEvent::class => AuthenticationEvents::AUTHENTICATION_SUCCESS,
81+
AuthenticationFailureEvent::class => AuthenticationEvents::AUTHENTICATION_FAILURE,
82+
InteractiveLoginEvent::class => SecurityEvents::INTERACTIVE_LOGIN,
83+
SwitchUserEvent::class => SecurityEvents::SWITCH_USER,
84+
]));
7185
}
7286
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\DependencyInjection;
13+
14+
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventSubscriber\TestSubscriber;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Extension\Extension;
17+
18+
final class EventExtension extends Extension
19+
{
20+
public function load(array $configs, ContainerBuilder $container): void
21+
{
22+
$container->register('test_subscriber', TestSubscriber::class)
23+
->setPublic(true)
24+
->addTag('kernel.event_subscriber');
25+
}
26+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
final class EventBundle extends Bundle
17+
{
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventSubscriber;
13+
14+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15+
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
16+
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
17+
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
18+
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
19+
20+
final class TestSubscriber implements EventSubscriberInterface
21+
{
22+
public $calledMethods = [];
23+
24+
public static function getSubscribedEvents(): array
25+
{
26+
return [
27+
AuthenticationSuccessEvent::class => 'onAuthenticationSuccess',
28+
AuthenticationFailureEvent::class => 'onAuthenticationFailure',
29+
InteractiveLoginEvent::class => 'onInteractiveLogin',
30+
SwitchUserEvent::class => 'onSwitchUser',
31+
];
32+
}
33+
34+
public function __call(string $name, array $arguments)
35+
{
36+
$this->calledMethods[$name] = ($this->calledMethods[$name] ?? 0) + 1;
37+
}
38+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
16+
use Symfony\Component\Security\Core\AuthenticationEvents;
17+
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
18+
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
19+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
20+
use Symfony\Component\Security\Core\User\UserInterface;
21+
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
22+
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
23+
use Symfony\Component\Security\Http\SecurityEvents;
24+
25+
final class EventAliasTest extends AbstractWebTestCase
26+
{
27+
public function testAliasedEvents(): void
28+
{
29+
$client = $this->createClient(['test_case' => 'AliasedEvents', 'root_config' => 'config.yml']);
30+
$container = $client->getContainer();
31+
$dispatcher = $container->get('event_dispatcher');
32+
33+
$dispatcher->dispatch(new AuthenticationSuccessEvent($this->createMock(TokenInterface::class)), AuthenticationEvents::AUTHENTICATION_SUCCESS);
34+
$dispatcher->dispatch(new AuthenticationFailureEvent($this->createMock(TokenInterface::class), new AuthenticationException()), AuthenticationEvents::AUTHENTICATION_FAILURE);
35+
$dispatcher->dispatch(new InteractiveLoginEvent($this->createMock(Request::class), $this->createMock(TokenInterface::class)), SecurityEvents::INTERACTIVE_LOGIN);
36+
$dispatcher->dispatch(new SwitchUserEvent($this->createMock(Request::class), $this->createMock(UserInterface::class), $this->createMock(TokenInterface::class)), SecurityEvents::SWITCH_USER);
37+
38+
$this->assertEquals(
39+
[
40+
'onAuthenticationSuccess' => 1,
41+
'onAuthenticationFailure' => 1,
42+
'onInteractiveLogin' => 1,
43+
'onSwitchUser' => 1,
44+
],
45+
$container->get('test_subscriber')->calledMethods
46+
);
47+
}
48+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
13+
use Symfony\Bundle\SecurityBundle\SecurityBundle;
14+
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventBundle;
15+
16+
return [
17+
new FrameworkBundle(),
18+
new SecurityBundle(),
19+
new EventBundle(),
20+
];
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imports:
2+
- { resource: ./../config/framework.yml }

src/Symfony/Component/EventDispatcher/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
4.4.0
5+
-----
6+
7+
* `AddEventAliasesPass` has been added, allowing applications and bundles to extend the event alias mapping used by `RegisterListenersPass`.
8+
49
4.3.0
510
-----
611

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\EventDispatcher\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
17+
/**
18+
* This pass allows bundles to extend the list of event aliases.
19+
*
20+
* @author Alexander M. Turek <me@derrabus.de>
21+
*/
22+
class AddEventAliasesPass implements CompilerPassInterface
23+
{
24+
private $eventAliases;
25+
private $eventAliasesParameter;
26+
27+
public function __construct(array $eventAliases, string $eventAliasesParameter = 'event_dispatcher.event_aliases')
28+
{
29+
$this->eventAliases = $eventAliases;
30+
$this->eventAliasesParameter = $eventAliasesParameter;
31+
}
32+
33+
public function process(ContainerBuilder $container): void
34+
{
35+
$eventAliases = $container->hasParameter($this->eventAliasesParameter) ? $container->getParameter($this->eventAliasesParameter) : [];
36+
37+
$container->setParameter(
38+
$this->eventAliasesParameter,
39+
array_merge($eventAliases, $this->eventAliases)
40+
);
41+
}
42+
}

src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
1819
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
1920
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
2021

@@ -67,6 +68,9 @@ public function testAliasedEventSubscriber(): void
6768
$builder->register('my_event_subscriber', AliasedSubscriber::class)
6869
->addTag('kernel.event_subscriber');
6970

71+
$eventAliasPass = new AddEventAliasesPass([CustomEvent::class => 'custom_event']);
72+
$eventAliasPass->process($builder);
73+
7074
$registerListenersPass = new RegisterListenersPass();
7175
$registerListenersPass->process($builder);
7276

@@ -79,6 +83,14 @@ public function testAliasedEventSubscriber(): void
7983
0,
8084
],
8185
],
86+
[
87+
'addListener',
88+
[
89+
'custom_event',
90+
[new ServiceClosureArgument(new Reference('my_event_subscriber')), 'onCustomEvent'],
91+
0,
92+
],
93+
],
8294
];
8395
$this->assertEquals($expectedCalls, $builder->getDefinition('event_dispatcher')->getMethodCalls());
8496
}
@@ -202,8 +214,12 @@ public function testAliasedEventListener(): void
202214
$container = new ContainerBuilder();
203215
$container->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
204216
$container->register('foo', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => AliasedEvent::class, 'method' => 'onEvent']);
217+
$container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => CustomEvent::class, 'method' => 'onEvent']);
205218
$container->register('event_dispatcher');
206219

220+
$eventAliasPass = new AddEventAliasesPass([CustomEvent::class => 'custom_event']);
221+
$eventAliasPass->process($container);
222+
207223
$registerListenersPass = new RegisterListenersPass();
208224
$registerListenersPass->process($container);
209225

@@ -217,6 +233,14 @@ public function testAliasedEventListener(): void
217233
0,
218234
],
219235
],
236+
[
237+
'addListener',
238+
[
239+
'custom_event',
240+
[new ServiceClosureArgument(new Reference('bar')), 'onEvent'],
241+
0,
242+
],
243+
],
220244
];
221245
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
222246
}
@@ -249,10 +273,15 @@ public static function getSubscribedEvents(): array
249273
{
250274
return [
251275
AliasedEvent::class => 'onAliasedEvent',
276+
CustomEvent::class => 'onCustomEvent',
252277
];
253278
}
254279
}
255280

256281
final class AliasedEvent
257282
{
258283
}
284+
285+
final class CustomEvent
286+
{
287+
}

src/Symfony/Component/HttpKernel/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"php": "^7.1.3",
2020
"symfony/error-handler": "^4.4|^5.0",
2121
"symfony/error-renderer": "^4.4|^5.0",
22-
"symfony/event-dispatcher": "^4.3",
22+
"symfony/event-dispatcher": "^4.4",
2323
"symfony/http-foundation": "^4.4|^5.0",
2424
"symfony/polyfill-ctype": "^1.8",
2525
"symfony/polyfill-php73": "^1.9",

0 commit comments

Comments
 (0)
0