feature #37295 Move event alias mappings to their components (derrabus)

This PR was merged into the 5.2-dev branch.

Discussion
----------

Move event alias mappings to their components

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | N/A
| License       | MIT
| Doc PR        | N/A

For a small console tool, I wanted to use the Console, EventDispatcher and DI components standalone. In order to make use of aliased events, I had to copy over the mapping information from FrameworkBundle, which was a bit unfortunate.

With this PR, I'd like to suggest to store all alias mappings on the `*Events` classes of each component instead. This change will make the mapping reusable outside of the full-stack framework.

Note: I've bumped the dependencies of FrameworkBundle/SercurityBundle in order to gain access to the moved mapping information. If any of the bumps is too heavy, please tell me and I'll implement a fallback instead.

Commits
-------

28e6f6f72c Move event alias mappings to their components.
This commit is contained in:
Fabien Potencier 2020-07-01 14:12:45 +02:00
commit 501542a0dd
11 changed files with 215 additions and 65 deletions

View File

@ -14,9 +14,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
use Symfony\Component\Config\ResourceCheckerConfigCacheFactory;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker;
use Symfony\Component\DependencyInjection\EnvVarProcessor;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag;
@ -26,70 +24,34 @@ use Symfony\Component\DependencyInjection\ReverseContainer;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface as EventDispatcherInterfaceComponentAlias;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\Event\PostSetDataEvent;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Event\PreSubmitEvent;
use Symfony\Component\Form\Event\SubmitEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\UrlHelper;
use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\EventListener\LocaleAwareListener;
use Symfony\Component\HttpKernel\HttpCache\Store;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\UriSigner;
use Symfony\Component\String\LazyString;
use Symfony\Component\String\Slugger\AsciiSlugger;
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Component\Workflow\Event\AnnounceEvent;
use Symfony\Component\Workflow\Event\CompletedEvent;
use Symfony\Component\Workflow\Event\EnteredEvent;
use Symfony\Component\Workflow\Event\EnterEvent;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Event\LeaveEvent;
use Symfony\Component\Workflow\Event\TransitionEvent;
use Symfony\Component\Workflow\WorkflowEvents;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
return static function (ContainerConfigurator $container) {
// this parameter is used at compile time in RegisterListenersPass
$container->parameters()->set('event_dispatcher.event_aliases', [
ConsoleCommandEvent::class => 'console.command',
ConsoleErrorEvent::class => 'console.error',
ConsoleTerminateEvent::class => 'console.terminate',
PreSubmitEvent::class => 'form.pre_submit',
SubmitEvent::class => 'form.submit',
PostSubmitEvent::class => 'form.post_submit',
PreSetDataEvent::class => 'form.pre_set_data',
PostSetDataEvent::class => 'form.post_set_data',
ControllerArgumentsEvent::class => 'kernel.controller_arguments',
ControllerEvent::class => 'kernel.controller',
ResponseEvent::class => 'kernel.response',
FinishRequestEvent::class => 'kernel.finish_request',
RequestEvent::class => 'kernel.request',
ViewEvent::class => 'kernel.view',
ExceptionEvent::class => 'kernel.exception',
TerminateEvent::class => 'kernel.terminate',
GuardEvent::class => 'workflow.guard',
LeaveEvent::class => 'workflow.leave',
TransitionEvent::class => 'workflow.transition',
EnterEvent::class => 'workflow.enter',
EnteredEvent::class => 'workflow.entered',
CompletedEvent::class => 'workflow.completed',
AnnounceEvent::class => 'workflow.announce',
]);
$container->parameters()->set('event_dispatcher.event_aliases', array_merge(
class_exists(ConsoleEvents::class) ? ConsoleEvents::ALIASES : [],
class_exists(FormEvents::class) ? FormEvents::ALIASES : [],
KernelEvents::ALIASES,
class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
));
$container->services()

View File

@ -36,12 +36,12 @@
"doctrine/cache": "~1.0",
"symfony/asset": "^5.1",
"symfony/browser-kit": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",
"symfony/console": "^5.2",
"symfony/css-selector": "^4.4|^5.0",
"symfony/dom-crawler": "^4.4|^5.0",
"symfony/dotenv": "^5.1",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/form": "^4.4|^5.0",
"symfony/form": "^5.2",
"symfony/expression-language": "^4.4|^5.0",
"symfony/http-client": "^4.4|^5.0",
"symfony/lock": "^4.4|^5.0",
@ -58,7 +58,7 @@
"symfony/translation": "^5.0",
"symfony/twig-bundle": "^4.4|^5.0",
"symfony/validator": "^4.4|^5.0",
"symfony/workflow": "^4.4|^5.0",
"symfony/workflow": "^5.2",
"symfony/yaml": "^4.4|^5.0",
"symfony/property-info": "^4.4|^5.0",
"symfony/web-link": "^4.4|^5.0",
@ -73,7 +73,7 @@
"phpunit/phpunit": "<5.4.3",
"symfony/asset": "<5.1",
"symfony/browser-kit": "<4.4",
"symfony/console": "<4.4",
"symfony/console": "<5.2",
"symfony/dotenv": "<5.1",
"symfony/dom-crawler": "<4.4",
"symfony/http-client": "<4.4",
@ -90,7 +90,7 @@
"symfony/twig-bundle": "<4.4",
"symfony/validator": "<4.4",
"symfony/web-profiler-bundle": "<4.4",
"symfony/workflow": "<4.4"
"symfony/workflow": "<5.2"
},
"suggest": {
"ext-apcu": "For best performance of the system caches",

View File

@ -37,10 +37,6 @@ 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;
/**
@ -79,11 +75,9 @@ class SecurityBundle extends Bundle
// must be registered after RegisterListenersPass (in the FrameworkBundle)
$container->addCompilerPass(new RegisterGlobalSecurityEventListenersPass(), PassConfig::TYPE_BEFORE_REMOVING, -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,
]));
$container->addCompilerPass(new AddEventAliasesPass(array_merge(
AuthenticationEvents::ALIASES,
SecurityEvents::ALIASES
)));
}
}

View File

@ -23,10 +23,10 @@
"symfony/event-dispatcher": "^5.1",
"symfony/http-kernel": "^5.0",
"symfony/polyfill-php80": "^1.15",
"symfony/security-core": "^5.1",
"symfony/security-core": "^5.2",
"symfony/security-csrf": "^4.4|^5.0",
"symfony/security-guard": "^5.1",
"symfony/security-http": "^5.1,>=5.1.2"
"symfony/security-http": "^5.2"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.0",

View File

@ -11,6 +11,10 @@
namespace Symfony\Component\Console;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
/**
* Contains all events dispatched by an Application.
*
@ -44,4 +48,15 @@ final class ConsoleEvents
* @Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
*/
const ERROR = 'console.error';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
ConsoleCommandEvent::class => self::COMMAND,
ConsoleErrorEvent::class => self::ERROR,
ConsoleTerminateEvent::class => self::TERMINATE,
];
}

View File

@ -0,0 +1,86 @@
<?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\Console\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\ApplicationTester;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ConsoleEventsTest extends TestCase
{
public function testEventAliases()
{
$container = new ContainerBuilder();
$container->setParameter('event_dispatcher.event_aliases', ConsoleEvents::ALIASES);
$container->addCompilerPass(new RegisterListenersPass());
$container->register('event_dispatcher', EventDispatcher::class);
$container->register('tracer', EventTraceSubscriber::class)
->setPublic(true)
->addTag('kernel.event_subscriber');
$container->register('failing_command', FailingCommand::class);
$container->register('application', Application::class)
->setPublic(true)
->addMethodCall('setAutoExit', [false])
->addMethodCall('setDispatcher', [new Reference('event_dispatcher')])
->addMethodCall('add', [new Reference('failing_command')])
;
$container->compile();
$tester = new ApplicationTester($container->get('application'));
$tester->run(['fail']);
$this->assertSame([ConsoleCommandEvent::class, ConsoleErrorEvent::class, ConsoleTerminateEvent::class], $container->get('tracer')->observedEvents);
}
}
class EventTraceSubscriber implements EventSubscriberInterface
{
public $observedEvents = [];
public static function getSubscribedEvents(): array
{
return [
ConsoleCommandEvent::class => 'observe',
ConsoleErrorEvent::class => 'observe',
ConsoleTerminateEvent::class => 'observe',
];
}
public function observe(object $event): void
{
$this->observedEvents[] = get_debug_type($event);
}
}
class FailingCommand extends Command
{
protected static $defaultName = 'fail';
protected function execute(InputInterface $input, OutputInterface $output): int
{
throw new \RuntimeException('I failed. Sorry.');
}
}

View File

@ -11,6 +11,12 @@
namespace Symfony\Component\Form;
use Symfony\Component\Form\Event\PostSetDataEvent;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Event\PreSubmitEvent;
use Symfony\Component\Form\Event\SubmitEvent;
/**
* To learn more about how form events work check the documentation
* entry at {@link https://symfony.com/doc/any/components/form/form_events.html}.
@ -88,6 +94,19 @@ final class FormEvents
*/
const POST_SET_DATA = 'form.post_set_data';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
PreSubmitEvent::class => self::PRE_SUBMIT,
SubmitEvent::class => self::SUBMIT,
PostSubmitEvent::class => self::POST_SUBMIT,
PreSetDataEvent::class => self::PRE_SET_DATA,
PostSetDataEvent::class => self::POST_SET_DATA,
];
private function __construct()
{
}

View File

@ -11,6 +11,15 @@
namespace Symfony\Component\HttpKernel;
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\Event\ViewEvent;
/**
* Contains all events thrown in the HttpKernel component.
*
@ -100,4 +109,20 @@ final class KernelEvents
* @Event("Symfony\Component\HttpKernel\Event\TerminateEvent")
*/
const TERMINATE = 'kernel.terminate';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
ControllerArgumentsEvent::class => self::CONTROLLER_ARGUMENTS,
ControllerEvent::class => self::CONTROLLER,
ResponseEvent::class => self::RESPONSE,
FinishRequestEvent::class => self::FINISH_REQUEST,
RequestEvent::class => self::REQUEST,
ViewEvent::class => self::VIEW,
ExceptionEvent::class => self::EXCEPTION,
TerminateEvent::class => self::TERMINATE,
];
}

View File

@ -11,6 +11,9 @@
namespace Symfony\Component\Security\Core;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
final class AuthenticationEvents
{
/**
@ -28,4 +31,14 @@ final class AuthenticationEvents
* @Event("Symfony\Component\Security\Core\Event\AuthenticationFailureEvent")
*/
const AUTHENTICATION_FAILURE = 'security.authentication.failure';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
AuthenticationSuccessEvent::class => self::AUTHENTICATION_SUCCESS,
AuthenticationFailureEvent::class => self::AUTHENTICATION_FAILURE,
];
}

View File

@ -11,6 +11,9 @@
namespace Symfony\Component\Security\Http;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
final class SecurityEvents
{
/**
@ -31,4 +34,14 @@ final class SecurityEvents
* @Event("Symfony\Component\Security\Http\Event\SwitchUserEvent")
*/
const SWITCH_USER = 'security.switch_user';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
InteractiveLoginEvent::class => self::INTERACTIVE_LOGIN,
SwitchUserEvent::class => self::SWITCH_USER,
];
}

View File

@ -11,6 +11,14 @@
namespace Symfony\Component\Workflow;
use Symfony\Component\Workflow\Event\AnnounceEvent;
use Symfony\Component\Workflow\Event\CompletedEvent;
use Symfony\Component\Workflow\Event\EnteredEvent;
use Symfony\Component\Workflow\Event\EnterEvent;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Event\LeaveEvent;
use Symfony\Component\Workflow\Event\TransitionEvent;
/**
* To learn more about how workflow events work, check the documentation
* entry at {@link https://symfony.com/doc/current/workflow/usage.html#using-events}.
@ -52,6 +60,21 @@ final class WorkflowEvents
*/
const TRANSITION = 'workflow.transition';
/**
* Event aliases.
*
* These aliases can be consumed by RegisterListenersPass.
*/
const ALIASES = [
GuardEvent::class => self::GUARD,
LeaveEvent::class => self::LEAVE,
TransitionEvent::class => self::TRANSITION,
EnterEvent::class => self::ENTER,
EnteredEvent::class => self::ENTERED,
CompletedEvent::class => self::COMPLETED,
AnnounceEvent::class => self::ANNOUNCE,
];
private function __construct()
{
}