Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fill listener method name, based on kernel.x event name in EventListenerToEventSubscriberRectory #694

Merged
merged 2 commits into from
Dec 8, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class SomeEventSubscriber implements \Symfony\Component\EventDispatcher\EventSub
*/
public static function getSubscribedEvents(): array
{
return ['some_event' => 'methodToBeCalled'];
return ['some_event' => 'methodToBeCalled', \Symfony\Component\HttpKernel\KernelEvents::EXCEPTION => 'onKernelException'];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<services>
<service id="first_listener" class="Rector\Symfony\Tests\CodeQuality\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\SomeListener">
<tag name="kernel.event_listener" event="some_event" method="methodToBeCalled"/>
<tag name="kernel.event_listener" event="kernel.exception"/>
</service>

<service id="second_listener" class="Rector\Symfony\Tests\CodeQuality\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\WithPriorityListener">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
use Rector\Rector\AbstractRector;
use Rector\Symfony\ApplicationMetadata\ListenerServiceDefinitionProvider;
use Rector\Symfony\Enum\SymfonyAttribute;
use Rector\Symfony\Enum\SymfonyClass;
use Rector\Symfony\NodeAnalyzer\ClassAnalyzer;
use Rector\Symfony\NodeFactory\GetSubscribedEventsClassMethodFactory;
use Rector\Symfony\ValueObject\EventNameToClassAndConstant;
Expand All @@ -24,26 +26,6 @@
*/
final class EventListenerToEventSubscriberRector extends AbstractRector
{
/**
* @var string
*/
private const EVENT_SUBSCRIBER_INTERFACE = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';

/**
* @var string
*/
private const EVENT_LISTENER_ATTRIBUTE = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';

/**
* @var string
*/
private const KERNEL_EVENTS_CLASS = 'Symfony\Component\HttpKernel\KernelEvents';

/**
* @var string
*/
private const CONSOLE_EVENTS_CLASS = 'Symfony\Component\Console\ConsoleEvents';

/**
* @var string
* @changelog https://regex101.com/r/qiHZ4T/1
Expand All @@ -63,22 +45,26 @@ public function __construct(
) {
$this->eventNamesToClassConstants = [
// kernel events
new EventNameToClassAndConstant('kernel.request', self::KERNEL_EVENTS_CLASS, 'REQUEST'),
new EventNameToClassAndConstant('kernel.exception', self::KERNEL_EVENTS_CLASS, 'EXCEPTION'),
new EventNameToClassAndConstant('kernel.view', self::KERNEL_EVENTS_CLASS, 'VIEW'),
new EventNameToClassAndConstant('kernel.controller', self::KERNEL_EVENTS_CLASS, 'CONTROLLER'),
new EventNameToClassAndConstant('kernel.request', SymfonyClass::KERNEL_EVENTS_CLASS, 'REQUEST'),
new EventNameToClassAndConstant('kernel.exception', SymfonyClass::KERNEL_EVENTS_CLASS, 'EXCEPTION'),
new EventNameToClassAndConstant('kernel.view', SymfonyClass::KERNEL_EVENTS_CLASS, 'VIEW'),
new EventNameToClassAndConstant('kernel.controller', SymfonyClass::KERNEL_EVENTS_CLASS, 'CONTROLLER'),
new EventNameToClassAndConstant(
'kernel.controller_arguments',
self::KERNEL_EVENTS_CLASS,
SymfonyClass::KERNEL_EVENTS_CLASS,
'CONTROLLER_ARGUMENTS'
),
new EventNameToClassAndConstant('kernel.response', self::KERNEL_EVENTS_CLASS, 'RESPONSE'),
new EventNameToClassAndConstant('kernel.terminate', self::KERNEL_EVENTS_CLASS, 'TERMINATE'),
new EventNameToClassAndConstant('kernel.finish_request', self::KERNEL_EVENTS_CLASS, 'FINISH_REQUEST'),
new EventNameToClassAndConstant('kernel.response', SymfonyClass::KERNEL_EVENTS_CLASS, 'RESPONSE'),
new EventNameToClassAndConstant('kernel.terminate', SymfonyClass::KERNEL_EVENTS_CLASS, 'TERMINATE'),
new EventNameToClassAndConstant(
'kernel.finish_request',
SymfonyClass::KERNEL_EVENTS_CLASS,
'FINISH_REQUEST'
),
// console events
new EventNameToClassAndConstant('console.command', self::CONSOLE_EVENTS_CLASS, 'COMMAND'),
new EventNameToClassAndConstant('console.terminate', self::CONSOLE_EVENTS_CLASS, 'TERMINATE'),
new EventNameToClassAndConstant('console.error', self::CONSOLE_EVENTS_CLASS, 'ERROR'),
new EventNameToClassAndConstant('console.command', SymfonyClass::CONSOLE_EVENTS_CLASS, 'COMMAND'),
new EventNameToClassAndConstant('console.terminate', SymfonyClass::CONSOLE_EVENTS_CLASS, 'TERMINATE'),
new EventNameToClassAndConstant('console.error', SymfonyClass::CONSOLE_EVENTS_CLASS, 'ERROR'),
];
}

Expand Down Expand Up @@ -139,17 +125,7 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): ?Node
{
// anonymous class
if (! $node->name instanceof Identifier) {
return null;
}

// is already a subscriber
if ($this->classAnalyzer->hasImplements($node, 'Symfony\Component\EventDispatcher\EventSubscriberInterface')) {
return null;
}

if ($this->hasAsListenerAttribute($node)) {
if ($this->shouldSkipClass($node)) {
return null;
}

Expand All @@ -173,7 +149,7 @@ public function refactor(Node $node): ?Node
*/
private function changeListenerToSubscriberWithMethods(Class_ $class, array $eventsToMethods): void
{
$class->implements[] = new FullyQualified(self::EVENT_SUBSCRIBER_INTERFACE);
$class->implements[] = new FullyQualified(SymfonyClass::EVENT_SUBSCRIBER_INTERFACE);

$classShortName = $this->nodeNameResolver->getShortName($class);

Expand All @@ -194,7 +170,7 @@ private function changeListenerToSubscriberWithMethods(Class_ $class, array $eve
*/
private function hasAsListenerAttribute(Class_ $class): bool
{
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, self::EVENT_LISTENER_ATTRIBUTE)) {
if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE)) {
return true;
}

Expand All @@ -203,11 +179,29 @@ private function hasAsListenerAttribute(Class_ $class): bool
continue;
}

if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, self::EVENT_LISTENER_ATTRIBUTE)) {
if ($this->phpAttributeAnalyzer->hasPhpAttribute(
$classMethod,
SymfonyAttribute::EVENT_LISTENER_ATTRIBUTE
)) {
return true;
}
}

return false;
}

private function shouldSkipClass(Class_ $class): bool
{
// anonymous class
if ($class->isAnonymous()) {
return true;
}

// is already a subscriber
if ($this->classAnalyzer->hasImplements($class, SymfonyClass::EVENT_SUBSCRIBER_INTERFACE)) {
return true;
}

return $this->hasAsListenerAttribute($class);
}
}
10 changes: 10 additions & 0 deletions src/ApplicationMetadata/ListenerServiceDefinitionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ public function extract(): array
}

$eventName = $tag->getEvent();

if ($tag->getMethod() === '') {
// fill method based on the event
if (str_starts_with($tag->getEvent(), 'kernel.')) {
[, $event] = explode('.', $tag->getEvent());
$methodName = 'onKernel' . ucfirst($event);
$tag->changeMethod($methodName);
}
}

$this->listenerClassesToEvents[$eventListener->getClass()][$eventName][] = $eventListener;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Enum/SymfonyAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ final class SymfonyAttribute
* @var string
*/
public const AUTOWIRE = 'Symfony\Component\DependencyInjection\Attribute\Autowire';

/**
* @var string
*/
public const EVENT_LISTENER_ATTRIBUTE = 'Symfony\Component\EventDispatcher\Attribute\AsEventListener';
}
15 changes: 15 additions & 0 deletions src/Enum/SymfonyClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,19 @@ final class SymfonyClass
* @var string
*/
public const SERIALIZER_INTERFACE = 'JMS\Serializer\SerializerInterface';

/**
* @var string
*/
public const KERNEL_EVENTS_CLASS = 'Symfony\Component\HttpKernel\KernelEvents';

/**
* @var string
*/
public const CONSOLE_EVENTS_CLASS = 'Symfony\Component\Console\ConsoleEvents';

/**
* @var string
*/
public const EVENT_SUBSCRIBER_INTERFACE = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
}
11 changes: 8 additions & 3 deletions src/ValueObject/Tag/EventListenerTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

use Rector\Symfony\Contract\Tag\TagInterface;

final readonly class EventListenerTag implements TagInterface
final class EventListenerTag implements TagInterface
{
public function __construct(
private string $event,
private readonly string $event,
private string $method,
private int $priority
private readonly int $priority
) {
}

Expand Down Expand Up @@ -46,4 +46,9 @@ public function getData(): array
'event' => $this->event,
];
}

public function changeMethod(string $methodName): void
{
$this->method = $methodName;
}
}
Loading