Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions UPGRADE-7.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ DoctrineBridge

* Deprecate `UniqueEntity::getRequiredOptions()` and `UniqueEntity::getDefaultOption()`

EventDispatcher
---------------
* Deprecate attribute `event` of the `kernel.event_listener` tag in favor of `events` attribute
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Deprecate attribute `event` of the `kernel.event_listener` tag in favor of `events` attribute
* Deprecate attribute `event` of the `kernel.event_listener` tag in favor of `events` attribute

Copy link
Contributor

Choose a reason for hiding this comment

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

There are also two spaces between "tag" and "in".

Copy link
Member

Choose a reason for hiding this comment

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

This deprecation must be reverted.


FrameworkBundle
---------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1205,9 +1205,9 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
if ($workflow['audit_trail']['enabled']) {
$listener = new Definition(Workflow\EventListener\AuditTrailListener::class);
$listener->addTag('monolog.logger', ['channel' => 'workflow']);
$listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.leave', $name), 'method' => 'onLeave']);
$listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.transition', $name), 'method' => 'onTransition']);
$listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.enter', $name), 'method' => 'onEnter']);
$listener->addTag('kernel.event_listener', ['events' => \sprintf('workflow.%s.leave', $name), 'method' => 'onLeave']);
$listener->addTag('kernel.event_listener', ['events' => \sprintf('workflow.%s.transition', $name), 'method' => 'onTransition']);
$listener->addTag('kernel.event_listener', ['events' => \sprintf('workflow.%s.enter', $name), 'method' => 'onEnter']);
$listener->addArgument(new Reference('logger'));
$container->setDefinition(\sprintf('.%s.listener.audit_trail', $workflowId), $listener);
}
Expand All @@ -1234,7 +1234,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
new Reference('validator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
]);
foreach ($guardsConfiguration as $eventName => $config) {
$guard->addTag('kernel.event_listener', ['event' => $eventName, 'method' => 'onTransition']);
$guard->addTag('kernel.event_listener', ['events' => $eventName, 'method' => 'onTransition']);
}

$container->setDefinition(\sprintf('.%s.listener.guard', $workflowId), $guard);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
->tag('data_collector', ['template' => '@WebProfiler/Collector/memory.html.twig', 'id' => 'memory', 'priority' => 325])

->set('data_collector.router', RouterDataCollector::class)
->tag('kernel.event_listener', ['event' => KernelEvents::CONTROLLER, 'method' => 'onKernelController'])
->tag('kernel.event_listener', ['events' => KernelEvents::CONTROLLER, 'method' => 'onKernelController'])
->tag('data_collector', ['template' => '@WebProfiler/Collector/router.html.twig', 'id' => 'router', 'priority' => 285])

->set('.data_collector.command', CommandDataCollector::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@
abstract_arg('framework.csrf_protection.cookie_name'),
])
->tag('monolog.logger', ['channel' => 'request'])
->tag('kernel.event_listener', ['event' => 'kernel.response', 'method' => 'onKernelResponse'])
->tag('kernel.event_listener', ['events' => 'kernel.response', 'method' => 'onKernelResponse'])
;
};
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ public function testWorkflowGuardExpressions()
$guardDefinition = $container->getDefinition('.workflow.article.listener.guard');
$this->assertSame([
[
'event' => 'workflow.article.guard.publish',
'events' => 'workflow.article.guard.publish',
'method' => 'onTransition',
],
], $guardDefinition->getTag('kernel.event_listener'));
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"symfony/dependency-injection": "^7.2|^8.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/error-handler": "^7.3|^8.0",
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
"symfony/event-dispatcher": "^7.4|^8.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm bumping the version to 7.4 in order to use the new events attribute instead of event.
Please let me know if you'd prefer a different strategy or approach. I'm happy to adjust and avoid the version bump if needed 😄

"symfony/http-foundation": "^7.3|^8.0",
"symfony/http-kernel": "^7.2|^8.0",
"symfony/polyfill-mbstring": "~1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
$defaultProvider = $providerIds[$normalizedName];

$container->setDefinition('security.listener.'.$id.'.user_provider', new ChildDefinition('security.listener.user_provider.abstract'))
->addTag('kernel.event_listener', ['dispatcher' => $firewallEventDispatcherId, 'event' => CheckPassportEvent::class, 'priority' => 2048, 'method' => 'checkPassport'])
->addTag('kernel.event_listener', ['dispatcher' => $firewallEventDispatcherId, 'events' => CheckPassportEvent::class, 'priority' => 2048, 'method' => 'checkPassport'])
->replaceArgument(0, new Reference($defaultProvider));
} elseif (1 === \count($providerIds)) {
$defaultProvider = reset($providerIds);
Expand Down Expand Up @@ -602,7 +602,7 @@ private function createContextListener(ContainerBuilder $container, string $cont
$listener->replaceArgument(2, $contextKey);
if (null !== $firewallEventDispatcherId) {
$listener->replaceArgument(4, new Reference($firewallEventDispatcherId));
$listener->addTag('kernel.event_listener', ['event' => KernelEvents::RESPONSE, 'method' => 'onKernelResponse']);
$listener->addTag('kernel.event_listener', ['events' => KernelEvents::RESPONSE, 'method' => 'onKernelResponse']);
}

return $this->contextListeners[$contextKey] = $listenerId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
->args([
service('security.user_providers'),
])
->tag('kernel.event_listener', ['event' => CheckPassportEvent::class, 'priority' => 1024, 'method' => 'checkPassport'])
->tag('kernel.event_listener', ['events' => CheckPassportEvent::class, 'priority' => 1024, 'method' => 'checkPassport'])

->set('security.listener.user_provider.abstract', UserProviderListener::class)
->abstract()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function testEventIsPropagated(string $configuredEvent, string $registere
]);

$this->container->register('app.security_listener', \stdClass::class)
->addTag('kernel.event_listener', ['method' => 'onEvent', 'event' => $configuredEvent]);
->addTag('kernel.event_listener', ['method' => 'onEvent', 'events' => $configuredEvent]);

$this->container->compile();

Expand Down Expand Up @@ -93,9 +93,9 @@ public function testRegisterCustomListener()
]);

$this->container->register('app.security_listener', \stdClass::class)
->addTag('kernel.event_listener', ['method' => 'onLogout', 'event' => LogoutEvent::class])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'event' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'event' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);
->addTag('kernel.event_listener', ['method' => 'onLogout', 'events' => LogoutEvent::class])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'events' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'events' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);

$this->container->compile();

Expand Down Expand Up @@ -136,9 +136,9 @@ public function testMultipleFirewalls()
->setPublic(true);

$this->container->register('app.security_listener', \stdClass::class)
->addTag('kernel.event_listener', ['method' => 'onLogout', 'event' => LogoutEvent::class])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'event' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'event' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);
->addTag('kernel.event_listener', ['method' => 'onLogout', 'events' => LogoutEvent::class])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'events' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'events' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);

$this->container->compile();

Expand All @@ -165,9 +165,9 @@ public function testListenerAlreadySpecific()
->setPublic(true);

$this->container->register('app.security_listener', \stdClass::class)
->addTag('kernel.event_listener', ['method' => 'onLogout', 'event' => LogoutEvent::class, 'dispatcher' => 'security.event_dispatcher.main'])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'event' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'event' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);
->addTag('kernel.event_listener', ['method' => 'onLogout', 'events' => LogoutEvent::class, 'dispatcher' => 'security.event_dispatcher.main'])
->addTag('kernel.event_listener', ['method' => 'onLoginSuccess', 'events' => LoginSuccessEvent::class, 'priority' => 20])
->addTag('kernel.event_listener', ['method' => 'onAuthenticationSuccess', 'events' => AuthenticationEvents::AUTHENTICATION_SUCCESS]);

$this->container->compile();

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/SecurityBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"symfony/config": "^7.3|^8.0",
"symfony/dependency-injection": "^6.4.11|^7.1.4|^8.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
"symfony/event-dispatcher": "^7.4|^8.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same here

"symfony/http-kernel": "^6.4|^7.0|^8.0",
"symfony/http-foundation": "^6.4|^7.0|^8.0",
"symfony/password-hasher": "^6.4|^7.0|^8.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
class AsEventListener
{
/**
* @param string|null $event The event name to listen to
* @param string|null $method The method to run when the listened event is triggered
* @param int $priority The priority of this listener if several are declared for the same event
* @param string|null $dispatcher The service id of the event dispatcher to listen to
* @param string|null $event The event name to listen to
* @param string|null $method The method to run when the listened event is triggered
* @param int $priority The priority of this listener if several are declared for the same event
* @param string|null $dispatcher The service id of the event dispatcher to listen to
* @param array|string|null $events The event or events name to listen to
*/
public function __construct(
public ?string $event = null,
public ?string $method = null,
public int $priority = 0,
public ?string $dispatcher = null,
public array|string|null $events = null,
) {
}
}
5 changes: 5 additions & 0 deletions src/Symfony/Component/EventDispatcher/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

7.4
---
* Add `events` attribute of the `kernel.event_listener` tag
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Add `events` attribute of the `kernel.event_listener` tag
* Add `events` attribute of the `kernel.event_listener` tag

* Deprecated the `event` attribute of the `kernel.event_listener` tag

6.0
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,52 @@ public function process(ContainerBuilder $container): void
$noPreload = 0;

foreach ($events as $event) {
if (isset($event['event'])) {
trigger_deprecation('symfony/event-dispatcher', '7.4', 'The "event" attribute of the `kernel.event_listener` tag is deprecated and will be removed in Symfony 8.0. Use "events" attribute instead.');
if (isset($event['events'])) {
throw new InvalidArgumentException('Cannot use both "event" and "events" attributes of the `kernel.event_listener` tag.');
}
$event['events'] = $event['event'];
}
$priority = $event['priority'] ?? 0;

if (!isset($event['event'])) {
if (!isset($event['events'])) {
if ($container->getDefinition($id)->hasTag('kernel.event_subscriber')) {
continue;
}

$event['method'] ??= '__invoke';
$event['event'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']);
$event['events'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']);
}

$event['event'] = $aliases[$event['event']] ?? $event['event'];
$event['events'] = array_map(fn (string $event) => $aliases[$event] ?? $event, (array) $event['events']);

if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace_callback([
'/(?<=\b|_)[a-z]/i',
'/[^a-z0-9]/i',
], fn ($matches) => strtoupper($matches[0]), $event['event']);
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);

if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method'])) {
if (!$r->hasMethod('__invoke')) {
throw new InvalidArgumentException(\sprintf('None of the "%s" or "__invoke" methods exist for the service "%s". Please define the "method" attribute on "kernel.event_listener" tags.', $event['method'], $id));
$class = $container->getDefinition($id)->getClass();
$reflectionClass = null !== $class ? $container->getReflectionClass($class, false) : null;
foreach ($event['events'] as $eventName) {
$methodName = 'on'.preg_replace_callback([
'/(?<=\b|_)[a-z]/i',
'/[^a-z0-9]/i',
], fn ($matches) => strtoupper($matches[0]), $eventName);
$methodName = preg_replace('/[^a-z0-9]/i', '', $methodName);

if ($reflectionClass) {
if ($reflectionClass->hasMethod($methodName)) {
$event['method'] = $methodName;
break;
}
if ($reflectionClass->hasMethod('__invoke')) {
$event['method'] = '__invoke';
break;
}
} else {
$event['method'] = $methodName;
break;
}
}

$event['method'] = '__invoke';
if (!isset($event['method'])) {
throw new InvalidArgumentException(\sprintf('None of the "%s" or "__invoke" methods exist for the service "%s". Please define the "method" attribute on "kernel.event_listener" tags.', $methodName, $id));
}
}

Expand All @@ -100,12 +120,13 @@ public function process(ContainerBuilder $container): void
$dispatcherDefinition = $container->findDefinition($event['dispatcher']);
}

$dispatcherDefinition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);

if (isset($this->hotPathEvents[$event['event']])) {
$container->getDefinition($id)->addTag('container.hot_path');
} elseif (isset($this->noPreloadEvents[$event['event']])) {
++$noPreload;
foreach ($event['events'] as $eventName) {
$dispatcherDefinition->addMethodCall('addListener', [$eventName, [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
if (isset($this->hotPathEvents[$eventName])) {
$container->getDefinition($id)->addTag('container.hot_path');
} elseif (isset($this->noPreloadEvents[$eventName])) {
++$noPreload;
}
}
}

Expand Down Expand Up @@ -167,21 +188,43 @@ public function process(ContainerBuilder $container): void
}
}

private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): string
/**
* @return string[]
*/
private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): array
{
if (
null === ($class = $container->getDefinition($id)->getClass())
|| !($r = $container->getReflectionClass($class, false))
|| !$r->hasMethod($method)
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
|| !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
|| $type->isBuiltin()
|| Event::class === ($name = $type->getName())
) {
throw new InvalidArgumentException(\sprintf('Service "%s" must define the "event" attribute on "kernel.event_listener" tags.', $id));
throw new InvalidArgumentException(\sprintf('Service "%s" must define the "events" attribute on "kernel.event_listener" tags.', $id));
}

$type = $m->getParameters()[0]->getType();
if ($type instanceof \ReflectionNamedType) {
if ($type->isBuiltin() || Event::class === ($name = $type->getName())) {
throw new InvalidArgumentException(\sprintf('Service "%s" must define the "events" attribute on "kernel.event_listener" tags.', $id));
}

return [$name];
}

if ($type instanceof \ReflectionUnionType) {
$types = [];
foreach ($type->getTypes() as $type) {
if (!$type->isBuiltin()) {
$types[] = $type->getName();
}
}

if ($types) {
return $types;
}
}

return $name;
throw new InvalidArgumentException(\sprintf('Service "%s" must define the "events" attribute on "kernel.event_listener" tags.', $id));
}
}

Expand Down
Loading