[EventDispatcher] A compiler pass for aliased userland events.

This commit is contained in:
Alexander M. Turek 2019-10-01 16:24:58 +02:00
parent 496346c88e
commit 34efe40371
13 changed files with 244 additions and 5 deletions

View File

@ -23,10 +23,6 @@
<parameter key="Symfony\Component\HttpKernel\Event\ViewEvent">kernel.view</parameter>
<parameter key="Symfony\Component\HttpKernel\Event\ExceptionEvent">kernel.exception</parameter>
<parameter key="Symfony\Component\HttpKernel\Event\TerminateEvent">kernel.terminate</parameter>
<parameter key="Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent">security.authentication.success</parameter>
<parameter key="Symfony\Component\Security\Core\Event\AuthenticationFailureEvent">security.authentication.failure</parameter>
<parameter key="Symfony\Component\Security\Http\Event\InteractiveLoginEvent">security.interactive_login</parameter>
<parameter key="Symfony\Component\Security\Http\Event\SwitchUserEvent">security.switch_user</parameter>
<parameter key="Symfony\Component\Workflow\Event\GuardEvent">workflow.guard</parameter>
<parameter key="Symfony\Component\Workflow\Event\LeaveEvent">workflow.leave</parameter>
<parameter key="Symfony\Component\Workflow\Event\TransitionEvent">workflow.transition</parameter>

View File

@ -78,6 +78,7 @@
"symfony/messenger": "<4.3",
"symfony/mime": "<4.4",
"symfony/property-info": "<3.4",
"symfony/security-bundle": "<4.4",
"symfony/serializer": "<4.2",
"symfony/stopwatch": "<3.4",
"symfony/translation": "<4.4",

View File

@ -33,7 +33,14 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMe
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\Security\Http\SecurityEvents;
/**
* Bundle.
@ -68,5 +75,12 @@ class SecurityBundle extends Bundle
$container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
$container->addCompilerPass(new RegisterTokenUsageTrackingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 200);
$container->addCompilerPass(new AddEventAliasesPass([
AuthenticationSuccessEvent::class => AuthenticationEvents::AUTHENTICATION_SUCCESS,
AuthenticationFailureEvent::class => AuthenticationEvents::AUTHENTICATION_FAILURE,
InteractiveLoginEvent::class => SecurityEvents::INTERACTIVE_LOGIN,
SwitchUserEvent::class => SecurityEvents::SWITCH_USER,
]));
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\DependencyInjection;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventSubscriber\TestSubscriber;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
final class EventExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$container->register('test_subscriber', TestSubscriber::class)
->setPublic(true)
->addTag('kernel.event_subscriber');
}
}

View File

@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
final class EventBundle extends Bundle
{
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
final class TestSubscriber implements EventSubscriberInterface
{
public $calledMethods = [];
public static function getSubscribedEvents(): array
{
return [
AuthenticationSuccessEvent::class => 'onAuthenticationSuccess',
AuthenticationFailureEvent::class => 'onAuthenticationFailure',
InteractiveLoginEvent::class => 'onInteractiveLogin',
SwitchUserEvent::class => 'onSwitchUser',
];
}
public function __call(string $name, array $arguments)
{
$this->calledMethods[$name] = ($this->calledMethods[$name] ?? 0) + 1;
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\Security\Http\SecurityEvents;
final class EventAliasTest extends AbstractWebTestCase
{
public function testAliasedEvents(): void
{
$client = $this->createClient(['test_case' => 'AliasedEvents', 'root_config' => 'config.yml']);
$container = $client->getContainer();
$dispatcher = $container->get('event_dispatcher');
$dispatcher->dispatch(new AuthenticationSuccessEvent($this->createMock(TokenInterface::class)), AuthenticationEvents::AUTHENTICATION_SUCCESS);
$dispatcher->dispatch(new AuthenticationFailureEvent($this->createMock(TokenInterface::class), new AuthenticationException()), AuthenticationEvents::AUTHENTICATION_FAILURE);
$dispatcher->dispatch(new InteractiveLoginEvent($this->createMock(Request::class), $this->createMock(TokenInterface::class)), SecurityEvents::INTERACTIVE_LOGIN);
$dispatcher->dispatch(new SwitchUserEvent($this->createMock(Request::class), $this->createMock(UserInterface::class), $this->createMock(TokenInterface::class)), SecurityEvents::SWITCH_USER);
$this->assertEquals(
[
'onAuthenticationSuccess' => 1,
'onAuthenticationFailure' => 1,
'onInteractiveLogin' => 1,
'onSwitchUser' => 1,
],
$container->get('test_subscriber')->calledMethods
);
}
}

View File

@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
new EventBundle(),
];

View File

@ -0,0 +1,2 @@
imports:
- { resource: ./../config/framework.yml }

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
4.4.0
-----
* `AddEventAliasesPass` has been added, allowing applications and bundles to extend the event alias mapping used by `RegisterListenersPass`.
4.3.0
-----

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* This pass allows bundles to extend the list of event aliases.
*
* @author Alexander M. Turek <me@derrabus.de>
*/
class AddEventAliasesPass implements CompilerPassInterface
{
private $eventAliases;
private $eventAliasesParameter;
public function __construct(array $eventAliases, string $eventAliasesParameter = 'event_dispatcher.event_aliases')
{
$this->eventAliases = $eventAliases;
$this->eventAliasesParameter = $eventAliasesParameter;
}
public function process(ContainerBuilder $container): void
{
$eventAliases = $container->hasParameter($this->eventAliasesParameter) ? $container->getParameter($this->eventAliasesParameter) : [];
$container->setParameter(
$this->eventAliasesParameter,
array_merge($eventAliases, $this->eventAliases)
);
}
}

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -67,6 +68,9 @@ class RegisterListenersPassTest extends TestCase
$builder->register('my_event_subscriber', AliasedSubscriber::class)
->addTag('kernel.event_subscriber');
$eventAliasPass = new AddEventAliasesPass([CustomEvent::class => 'custom_event']);
$eventAliasPass->process($builder);
$registerListenersPass = new RegisterListenersPass();
$registerListenersPass->process($builder);
@ -79,6 +83,14 @@ class RegisterListenersPassTest extends TestCase
0,
],
],
[
'addListener',
[
'custom_event',
[new ServiceClosureArgument(new Reference('my_event_subscriber')), 'onCustomEvent'],
0,
],
],
];
$this->assertEquals($expectedCalls, $builder->getDefinition('event_dispatcher')->getMethodCalls());
}
@ -202,8 +214,12 @@ class RegisterListenersPassTest extends TestCase
$container = new ContainerBuilder();
$container->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
$container->register('foo', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => AliasedEvent::class, 'method' => 'onEvent']);
$container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => CustomEvent::class, 'method' => 'onEvent']);
$container->register('event_dispatcher');
$eventAliasPass = new AddEventAliasesPass([CustomEvent::class => 'custom_event']);
$eventAliasPass->process($container);
$registerListenersPass = new RegisterListenersPass();
$registerListenersPass->process($container);
@ -217,6 +233,14 @@ class RegisterListenersPassTest extends TestCase
0,
],
],
[
'addListener',
[
'custom_event',
[new ServiceClosureArgument(new Reference('bar')), 'onEvent'],
0,
],
],
];
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
}
@ -249,6 +273,7 @@ final class AliasedSubscriber implements EventSubscriberInterface
{
return [
AliasedEvent::class => 'onAliasedEvent',
CustomEvent::class => 'onCustomEvent',
];
}
}
@ -256,3 +281,7 @@ final class AliasedSubscriber implements EventSubscriberInterface
final class AliasedEvent
{
}
final class CustomEvent
{
}

View File

@ -19,7 +19,7 @@
"php": "^7.1.3",
"symfony/error-handler": "^4.4|^5.0",
"symfony/error-renderer": "^4.4|^5.0",
"symfony/event-dispatcher": "^4.3",
"symfony/event-dispatcher": "^4.4",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-php73": "^1.9",