Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5c76029

Browse files
bug#62184 [EventDispatcher][FrameworkBundle] Rework union types on#[AsEventListener] (HypeMC)
This PR was merged into the 7.4 branch.Discussion----------[EventDispatcher][FrameworkBundle] Rework union types on `#[AsEventListener]`| Q | A| ------------- | ---| Branch? | 7.4| Bug fix? | yes| New feature? | no| Deprecations? | no| Issues | -| License | MITThis is a reimplementation of#61252.The previous PR introduced a backward compatibility break.Consider the following listener:```phpfinal class TestListener{ #[AsEventListener(event: RequestEvent::class)] public function onRequestEvent(): void { // ... }}```In earlier versions, this worked fine, but now it throws:> AsEventListener attribute requires the first argument of "App\EventListener\TestListener::onRequestEvent()" to be an event object.Interestingly, there *was* a test for this scenario, but since each test method re-defines the `registerAttributeForAutoconfiguration()` closure (which wasn't updated everywhere), the tests still passed.Additionally, the implementation was added to the `FrameworkExtension`, even though similar logic already existed in `RegisterListenersPass::getEventFromTypeDeclaration()`, resulting in a decentralized implementation.This PR reverts the changes in `FrameworkExtension` and re-implements the feature in `RegisterListenersPass`.The tests now reuse the closure from `FrameworkExtension` to make them more robust and consistent with the actual implementation.Commits-------8ea7196 [EventDispatcher][FrameworkBundle] Rework union types on `#[AsEventListener]`
2 parents99bbae9 +8ea7196 commit5c76029

File tree

4 files changed

+60
-83
lines changed

4 files changed

+60
-83
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php‎

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -779,32 +779,13 @@ public function load(array $configs, ContainerBuilder $container): void
779779

780780
$container->registerAttributeForAutoconfiguration(AsEventListener::class,staticfunction (ChildDefinition$definition,AsEventListener$attribute,\ReflectionClass|\ReflectionMethod$reflector) {
781781
$tagAttributes =get_object_vars($attribute);
782-
783-
if (!$reflectorinstanceof \ReflectionMethod) {
784-
$definition->addTag('kernel.event_listener',$tagAttributes);
785-
786-
return;
787-
}
788-
789-
if (isset($tagAttributes['method'])) {
790-
thrownewLogicException(\sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".',$reflector->class,$reflector->name));
791-
}
792-
793-
$tagAttributes['method'] =$reflector->getName();
794-
795-
if (!$eventArg =$reflector->getParameters()[0] ??null) {
796-
thrownewLogicException(\sprintf('AsEventListener attribute requires the first argument of "%s::%s()" to be an event object.',$reflector->class,$reflector->name));
797-
}
798-
799-
$types = ($type =$eventArg->getType()instanceof \ReflectionUnionType ?$eventArg->getType()->getTypes() : [$eventArg->getType()]) ?: [];
800-
801-
foreach ($typesas$type) {
802-
if ($typeinstanceof \ReflectionNamedType && !$type->isBuiltin()) {
803-
$tagAttributes['event'] =$type->getName();
804-
805-
$definition->addTag('kernel.event_listener',$tagAttributes);
782+
if ($reflectorinstanceof \ReflectionMethod) {
783+
if (isset($tagAttributes['method'])) {
784+
thrownewLogicException(\sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".',$reflector->class,$reflector->name));
806785
}
786+
$tagAttributes['method'] =$reflector->getName();
807787
}
788+
$definition->addTag('kernel.event_listener',$tagAttributes);
808789
});
809790
$container->registerAttributeForAutoconfiguration(AsController::class,staticfunction (ChildDefinition$definition,AsController$attribute):void {
810791
$definition->addTag('controller.service_arguments');

‎src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php‎

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,27 @@ public function process(ContainerBuilder $container): void
6565
foreach ($container->findTaggedServiceIds('kernel.event_listener',true)as$id =>$events) {
6666
$noPreload =0;
6767

68+
$resolvedEvents = [];
6869
foreach ($eventsas$event) {
69-
$priority =$event['priority'] ??0;
70-
7170
if (!isset($event['event'])) {
7271
if ($container->getDefinition($id)->hasTag('kernel.event_subscriber')) {
7372
continue;
7473
}
7574

7675
$event['method'] ??='__invoke';
77-
$event['event'] =$this->getEventFromTypeDeclaration($container,$id,$event['method']);
76+
$eventNames =$this->getEventFromTypeDeclaration($container,$id,$event['method']);
77+
}else {
78+
$eventNames = [$event['event']];
79+
}
80+
81+
foreach ($eventNamesas$eventName) {
82+
$event['event'] =$aliases[$eventName] ??$eventName;
83+
$resolvedEvents[] =$event;
7884
}
85+
}
7986

80-
$event['event'] =$aliases[$event['event']] ??$event['event'];
87+
foreach ($resolvedEventsas$event) {
88+
$priority =$event['priority'] ??0;
8189

8290
if (!isset($event['method'])) {
8391
$event['method'] ='on'.preg_replace_callback([
@@ -167,21 +175,40 @@ public function process(ContainerBuilder $container): void
167175
}
168176
}
169177

170-
privatefunctiongetEventFromTypeDeclaration(ContainerBuilder$container,string$id,string$method):string
178+
/**
179+
* @return string[]
180+
*/
181+
privatefunctiongetEventFromTypeDeclaration(ContainerBuilder$container,string$id,string$method):array
171182
{
172183
if (
173184
null === ($class =$container->getDefinition($id)->getClass())
174185
|| !($r =$container->getReflectionClass($class,false))
175186
|| !$r->hasMethod($method)
176187
||1 > ($m =$r->getMethod($method))->getNumberOfParameters()
177-
|| !($type =$m->getParameters()[0]->getType())instanceof \ReflectionNamedType
178-
||$type->isBuiltin()
179-
|| Event::class === ($name =$type->getName())
188+
|| !(($type =$m->getParameters()[0]->getType())instanceof \ReflectionNamedType ||$typeinstanceof \ReflectionUnionType)
180189
) {
181190
thrownewInvalidArgumentException(\sprintf('Service "%s" must define the "event" attribute on "kernel.event_listener" tags.',$id));
182191
}
183192

184-
return$name;
193+
$types =$typeinstanceof \ReflectionUnionType ?$type->getTypes() : [$type];
194+
195+
$names = [];
196+
foreach ($typesas$type) {
197+
if (!$typeinstanceof \ReflectionNamedType
198+
||$type->isBuiltin()
199+
|| Event::class === ($name =$type->getName())
200+
) {
201+
continue;
202+
}
203+
204+
$names[] =$name;
205+
}
206+
207+
if (!$names) {
208+
thrownewInvalidArgumentException(\sprintf('Service "%s" must define the "event" attribute on "kernel.event_listener" tags.',$id));
209+
}
210+
211+
return$names;
185212
}
186213
}
187214

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

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@
1212
namespaceSymfony\Component\EventDispatcher\Tests\DependencyInjection;
1313

1414
usePHPUnit\Framework\TestCase;
15+
useSymfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
1516
useSymfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
16-
useSymfony\Component\DependencyInjection\ChildDefinition;
1717
useSymfony\Component\DependencyInjection\Compiler\AttributeAutoconfigurationPass;
1818
useSymfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
1919
useSymfony\Component\DependencyInjection\ContainerBuilder;
2020
useSymfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2121
useSymfony\Component\DependencyInjection\Reference;
22-
useSymfony\Component\EventDispatcher\Attribute\AsEventListener;
2322
useSymfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
2423
useSymfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
2524
useSymfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -275,10 +274,7 @@ public function testItThrowsAnExceptionIfTagIsMissingMethodAndClassHasNoValidMet
275274

276275
publicfunctiontestTaggedInvokableEventListener()
277276
{
278-
$container =newContainerBuilder();
279-
$container->registerAttributeForAutoconfiguration(AsEventListener::class,staticfunction (ChildDefinition$definition,AsEventListener$attribute):void {
280-
$definition->addTag('kernel.event_listener',get_object_vars($attribute));
281-
});
277+
$container =$this->createContainerBuilder();
282278
$container->register('foo', TaggedInvokableListener::class)->setAutoconfigured(true);
283279
$container->register('event_dispatcher', \stdClass::class);
284280

@@ -297,21 +293,12 @@ public function testTaggedInvokableEventListener()
297293
],
298294
],
299295
];
300-
$this->assertEquals($expectedCalls,$definition->getMethodCalls());
296+
$this->assertEquals($expectedCalls,\array_slice($definition->getMethodCalls(),0,\count($expectedCalls)));
301297
}
302298

303299
publicfunctiontestTaggedMultiEventListener()
304300
{
305-
$container =newContainerBuilder();
306-
$container->registerAttributeForAutoconfiguration(AsEventListener::class,
307-
staticfunction (ChildDefinition$definition,AsEventListener$attribute,\ReflectionClass|\ReflectionMethod$reflector):void {
308-
$tagAttributes =get_object_vars($attribute);
309-
if ($reflectorinstanceof \ReflectionMethod) {
310-
$tagAttributes['method'] =$reflector->getName();
311-
}
312-
$definition->addTag('kernel.event_listener',$tagAttributes);
313-
}
314-
);
301+
$container =$this->createContainerBuilder();
315302

316303
$container->register('foo', TaggedMultiListener::class)->setAutoconfigured(true);
317304
$container->register('event_dispatcher', \stdClass::class);
@@ -355,42 +342,12 @@ static function (ChildDefinition $definition, AsEventListener $attribute, \Refle
355342
],
356343
],
357344
];
358-
$this->assertEquals($expectedCalls,$definition->getMethodCalls());
345+
$this->assertEquals($expectedCalls,\array_slice($definition->getMethodCalls(),0,\count($expectedCalls)));
359346
}
360347

361348
publicfunctiontestTaggedMethodUnionTypeEventListener()
362349
{
363-
$container =newContainerBuilder();
364-
$container->registerAttributeForAutoconfiguration(AsEventListener::class,
365-
staticfunction (ChildDefinition$definition,AsEventListener$attribute,\ReflectionClass|\ReflectionMethod$reflector) {
366-
$tagAttributes =get_object_vars($attribute);
367-
368-
if (!$reflectorinstanceof \ReflectionMethod) {
369-
$definition->addTag('kernel.event_listener',$tagAttributes);
370-
371-
return;
372-
}
373-
374-
if (isset($tagAttributes['method'])) {
375-
thrownew \LogicException(\sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".',$reflector->class,$reflector->name));
376-
}
377-
378-
$tagAttributes['method'] =$reflector->getName();
379-
380-
if (!$eventArg =$reflector->getParameters()[0] ??null) {
381-
thrownew \LogicException(\sprintf('AsEventListener attribute requires the first argument of "%s::%s()" to be an event object.',$reflector->class,$reflector->name));
382-
}
383-
384-
$types = ($type =$eventArg->getType()instanceof \ReflectionUnionType ?$eventArg->getType()->getTypes() : [$eventArg->getType()]) ?: [];
385-
386-
foreach ($typesas$type) {
387-
if ($typeinstanceof \ReflectionNamedType && !$type->isBuiltin()) {
388-
$tagAttributes['event'] =$type->getName();
389-
390-
$definition->addTag('kernel.event_listener',$tagAttributes);
391-
}
392-
}
393-
});
350+
$container =$this->createContainerBuilder();
394351

395352
$container->register('foo', TaggedUnionTypeListener::class)->setAutoconfigured(true);
396353
$container->register('event_dispatcher', \stdClass::class);
@@ -419,7 +376,7 @@ static function (ChildDefinition $definition, AsEventListener $attribute, \Refle
419376
],
420377
];
421378

422-
$this->assertEquals($expectedCalls,$definition->getMethodCalls());
379+
$this->assertEquals($expectedCalls,\array_slice($definition->getMethodCalls(),0,\count($expectedCalls)));
423380
}
424381

425382
publicfunctiontestAliasedEventListener()
@@ -569,6 +526,17 @@ public function testOmitEventNameOnSubscriber()
569526
];
570527
$this->assertEquals($expectedCalls,$definition->getMethodCalls());
571528
}
529+
530+
privatefunctioncreateContainerBuilder():ContainerBuilder
531+
{
532+
$container =newContainerBuilder();
533+
$container->setParameter('kernel.debug',true);
534+
$container->setParameter('kernel.project_dir',__DIR__);
535+
$container->setParameter('kernel.container_class','testContainer');
536+
(newFrameworkExtension())->load([],$container);
537+
538+
return$container;
539+
}
572540
}
573541

574542
class SubscriberServiceimplements EventSubscriberInterface

‎src/Symfony/Component/EventDispatcher/composer.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"symfony/expression-language":"^6.4|^7.0|^8.0",
2525
"symfony/config":"^6.4|^7.0|^8.0",
2626
"symfony/error-handler":"^6.4|^7.0|^8.0",
27+
"symfony/framework-bundle":"^6.4|^7.0|^8.0",
2728
"symfony/http-foundation":"^6.4|^7.0|^8.0",
2829
"symfony/service-contracts":"^2.5|^3",
2930
"symfony/stopwatch":"^6.4|^7.0|^8.0",

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp