mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-08 04:10:51 +00:00
[Symfony 5.2] Add LogoutHandlerToLogoutEventSubscriberRector (#5337)
This commit is contained in:
parent
a54819aba8
commit
e0db712dec
|
@ -65,6 +65,8 @@
|
|||
"phpstan/phpstan-nette": "^0.12.12",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"sebastian/diff": "^4.0.4",
|
||||
"symfony/security-core": "^5.2",
|
||||
"symfony/security-http": "^5.2",
|
||||
"symplify/changelog-linker": "^9.0.34",
|
||||
"symplify/coding-standard": "^9.0.34",
|
||||
"symplify/easy-coding-standard": "^9.0.34",
|
||||
|
|
|
@ -147,6 +147,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
// @see https://github.com/symfony/symfony/pull/35858
|
||||
RenameStringRector::STRING_CHANGES => [
|
||||
'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR',
|
||||
]
|
||||
],
|
||||
]]);
|
||||
};
|
||||
|
|
|
@ -127,7 +127,7 @@ final class TemplateVariablesFactory
|
|||
{
|
||||
if (Strings::contains($code, PHP_EOL)) {
|
||||
// multi lines
|
||||
return sprintf("<<<'PHP'%s%s%sPHP%s", PHP_EOL, $code, PHP_EOL, PHP_EOL);
|
||||
return sprintf("<<<'CODE_SAMPLE'%s%s%sCODE_SAMPLE%s", PHP_EOL, $code, PHP_EOL, PHP_EOL);
|
||||
}
|
||||
|
||||
// single line
|
||||
|
|
|
@ -30,7 +30,7 @@ private $classTypeToMethodName = [];
|
|||
{
|
||||
return new RuleDefinition('Change $service->arg(...) to $service->call(...)', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
@ -39,9 +39,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(SomeClass::class)
|
||||
->arg('$key', 'value');
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
@ -52,7 +52,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
'$key' => 'value'
|
||||
]]);
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[self::CLASS_TYPE_TO_METHOD_NAME => ['SomeClass' => 'configure']]
|
||||
)
|
||||
|
|
|
@ -30,7 +30,7 @@ private $classTypeToMethodName = [];
|
|||
{
|
||||
return new RuleDefinition('Change $service->arg(...) to $service->call(...)', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
@ -39,9 +39,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(SomeClass::class)
|
||||
->arg('$key', 'value');
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
@ -52,7 +52,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
'$key' => 'value'
|
||||
]]);
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[self::CLASS_TYPE_TO_METHOD_NAME => ['SomeClass' => 'configure']]
|
||||
)
|
||||
|
|
|
@ -19,7 +19,7 @@ final class TestRector extends AbstractRector
|
|||
{
|
||||
return new RuleDefinition('Description', [
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
|
@ -27,10 +27,10 @@ class SomeClass
|
|||
$this->something();
|
||||
}
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'PHP'
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
|
@ -38,7 +38,7 @@ class SomeClass
|
|||
$this->somethingElse();
|
||||
}
|
||||
}
|
||||
PHP
|
||||
CODE_SAMPLE
|
||||
|
||||
)
|
||||
]);
|
||||
|
|
|
@ -154,8 +154,11 @@ CODE_SAMPLE
|
|||
);
|
||||
}
|
||||
|
||||
private function removeOrReplaceImplementedInterface(string $implementedInterfaceName, Class_ $class, int $key): void
|
||||
{
|
||||
private function removeOrReplaceImplementedInterface(
|
||||
string $implementedInterfaceName,
|
||||
Class_ $class,
|
||||
int $key
|
||||
): void {
|
||||
$parentInterface = $this->getParentInterfaceIfFound($implementedInterfaceName);
|
||||
if ($parentInterface !== null) {
|
||||
$class->implements[$key] = new FullyQualified($parentInterface);
|
||||
|
|
|
@ -116,16 +116,7 @@ final class ListeningClassMethodArgumentManipulator
|
|||
}
|
||||
}
|
||||
|
||||
private function changeClassParamToEventClass(string $eventClass, ClassMethod $classMethod): void
|
||||
{
|
||||
$paramName = $this->classNaming->getVariableName($eventClass);
|
||||
$eventVariable = new Variable($paramName);
|
||||
|
||||
$param = new Param($eventVariable, null, new FullyQualified($eventClass));
|
||||
$classMethod->params = [$param];
|
||||
}
|
||||
|
||||
private function isParamUsedInClassMethodBody(ClassMethod $classMethod, Param $param): bool
|
||||
public function isParamUsedInClassMethodBody(ClassMethod $classMethod, Param $param): bool
|
||||
{
|
||||
return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use (
|
||||
$param
|
||||
|
@ -138,6 +129,15 @@ final class ListeningClassMethodArgumentManipulator
|
|||
});
|
||||
}
|
||||
|
||||
private function changeClassParamToEventClass(string $eventClass, ClassMethod $classMethod): void
|
||||
{
|
||||
$paramName = $this->classNaming->getVariableName($eventClass);
|
||||
$eventVariable = new Variable($paramName);
|
||||
|
||||
$param = new Param($eventVariable, null, new FullyQualified($eventClass));
|
||||
$classMethod->params = [$param];
|
||||
}
|
||||
|
||||
private function createEventGetterToVariableMethodCall(
|
||||
string $eventClass,
|
||||
Param $param,
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\NodeFactory;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\SymfonyCodeQuality\ValueObject\EventNameToClassAndConstant;
|
||||
|
||||
final class EventReferenceFactory
|
||||
{
|
||||
/**
|
||||
* @var NodeFactory
|
||||
*/
|
||||
private $nodeFactory;
|
||||
|
||||
public function __construct(NodeFactory $nodeFactory)
|
||||
{
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventNameToClassAndConstant[] $eventNamesToClassConstants
|
||||
* @return String_|ClassConstFetch
|
||||
*/
|
||||
public function createEventName(string $eventName, array $eventNamesToClassConstants): Node
|
||||
{
|
||||
if (class_exists($eventName)) {
|
||||
return $this->nodeFactory->createClassConstReference($eventName);
|
||||
}
|
||||
|
||||
// is string a that could be caught in constant, e.g. KernelEvents?
|
||||
foreach ($eventNamesToClassConstants as $eventNameToClassConstant) {
|
||||
if ($eventNameToClassConstant->getEventName() !== $eventName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->nodeFactory->createClassConstFetch(
|
||||
$eventNameToClassConstant->getEventClass(),
|
||||
$eventNameToClassConstant->getEventConstant()
|
||||
);
|
||||
}
|
||||
|
||||
return new String_($eventName);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\SymfonyCodeQuality\NodeFactory;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
|
@ -16,6 +15,7 @@ use PhpParser\Node\Stmt\ClassMethod;
|
|||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\Core\Php\PhpVersionProvider;
|
||||
|
@ -27,9 +27,15 @@ use Rector\Symfony\ValueObject\ServiceDefinition;
|
|||
use Rector\Symfony\ValueObject\Tag;
|
||||
use Rector\Symfony\ValueObject\Tag\EventListenerTag;
|
||||
use Rector\SymfonyCodeQuality\ValueObject\EventNameToClassAndConstant;
|
||||
use Rector\SymfonyCodeQuality\ValueObject\EventReferenceToMethodName;
|
||||
|
||||
final class GetSubscriberEventsClassMethodFactory
|
||||
final class GetSubscribedEventsClassMethodFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const GET_SUBSCRIBED_EVENTS_METHOD_NAME = 'getSubscribedEvents';
|
||||
|
||||
/**
|
||||
* @var NodeFactory
|
||||
*/
|
||||
|
@ -55,34 +61,70 @@ final class GetSubscriberEventsClassMethodFactory
|
|||
*/
|
||||
private $phpDocTypeChanger;
|
||||
|
||||
/**
|
||||
* @var EventReferenceFactory
|
||||
*/
|
||||
private $eventReferenceFactory;
|
||||
|
||||
public function __construct(
|
||||
NodeFactory $nodeFactory,
|
||||
VisibilityManipulator $visibilityManipulator,
|
||||
PhpVersionProvider $phpVersionProvider,
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocTypeChanger $phpDocTypeChanger
|
||||
PhpDocTypeChanger $phpDocTypeChanger,
|
||||
EventReferenceFactory $eventReferenceFactory
|
||||
) {
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->visibilityManipulator = $visibilityManipulator;
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
$this->eventReferenceFactory = $eventReferenceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventReferenceToMethodName[] $eventReferencesToMethodNames
|
||||
*/
|
||||
public function create(array $eventReferencesToMethodNames): ClassMethod
|
||||
{
|
||||
$getSubscribersClassMethod = $this->createClassMethod();
|
||||
|
||||
$eventsToMethodsArray = new Array_();
|
||||
|
||||
foreach ($eventReferencesToMethodNames as $eventReferencesToMethodName) {
|
||||
$eventsToMethodsArray->items[] = $this->createArrayItemFromMethodAndPriority(
|
||||
null,
|
||||
$eventReferencesToMethodName->getMethodName(),
|
||||
$eventReferencesToMethodName->getClassConstFetch()
|
||||
);
|
||||
}
|
||||
|
||||
$getSubscribersClassMethod->stmts[] = new Return_($eventsToMethodsArray);
|
||||
|
||||
$this->decorateClassMethodWithReturnType($getSubscribersClassMethod);
|
||||
|
||||
return $getSubscribersClassMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ServiceDefinition[]> $eventsToMethods
|
||||
* @param EventNameToClassAndConstant[] $eventNamesToClassConstants
|
||||
*/
|
||||
public function createFromEventsToMethods(array $eventsToMethods, array $eventNamesToClassConstants): ClassMethod
|
||||
{
|
||||
$getSubscribersClassMethod = $this->nodeFactory->createPublicMethod('getSubscribedEvents');
|
||||
public function createFromServiceDefinitionsAndEventsToMethods(
|
||||
array $eventsToMethods,
|
||||
array $eventNamesToClassConstants
|
||||
): ClassMethod {
|
||||
$getSubscribersClassMethod = $this->createClassMethod();
|
||||
|
||||
$eventsToMethodsArray = new Array_();
|
||||
|
||||
$this->visibilityManipulator->makeStatic($getSubscribersClassMethod);
|
||||
|
||||
foreach ($eventsToMethods as $eventName => $methodNamesWithPriorities) {
|
||||
$eventNameExpr = $this->createEventName($eventName, $eventNamesToClassConstants);
|
||||
$eventNameExpr = $this->eventReferenceFactory->createEventName($eventName, $eventNamesToClassConstants);
|
||||
|
||||
// just method name, without a priority
|
||||
if (! is_array($methodNamesWithPriorities)) {
|
||||
$methodNamesWithPriorities = [$methodNamesWithPriorities];
|
||||
}
|
||||
|
||||
if (count($methodNamesWithPriorities) === 1) {
|
||||
$this->createSingleMethod(
|
||||
|
@ -107,29 +149,36 @@ final class GetSubscriberEventsClassMethodFactory
|
|||
return $getSubscribersClassMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventNameToClassAndConstant[] $eventNamesToClassConstants
|
||||
* @return String_|ClassConstFetch
|
||||
*/
|
||||
private function createEventName(string $eventName, array $eventNamesToClassConstants): Node
|
||||
private function createClassMethod(): ClassMethod
|
||||
{
|
||||
if (class_exists($eventName)) {
|
||||
return $this->nodeFactory->createClassConstReference($eventName);
|
||||
$classMethod = $this->nodeFactory->createPublicMethod(self::GET_SUBSCRIBED_EVENTS_METHOD_NAME);
|
||||
$this->visibilityManipulator->makeStatic($classMethod);
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
private function createArrayItemFromMethodAndPriority(?int $priority, string $methodName, Expr $expr): ArrayItem
|
||||
{
|
||||
if ($priority !== null && $priority !== 0) {
|
||||
$methodNameWithPriorityArray = new Array_();
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new String_($methodName));
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new LNumber((int) $priority));
|
||||
|
||||
return new ArrayItem($methodNameWithPriorityArray, $expr);
|
||||
}
|
||||
|
||||
// is string a that could be caught in constant, e.g. KernelEvents?
|
||||
foreach ($eventNamesToClassConstants as $eventNameToClassConstant) {
|
||||
if ($eventNameToClassConstant->getEventName() !== $eventName) {
|
||||
continue;
|
||||
}
|
||||
return new ArrayItem(new String_($methodName), $expr);
|
||||
}
|
||||
|
||||
return $this->nodeFactory->createClassConstFetch(
|
||||
$eventNameToClassConstant->getEventClass(),
|
||||
$eventNameToClassConstant->getEventConstant()
|
||||
);
|
||||
private function decorateClassMethodWithReturnType(ClassMethod $classMethod): void
|
||||
{
|
||||
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
|
||||
$classMethod->returnType = new Identifier('array');
|
||||
}
|
||||
|
||||
return new String_($eventName);
|
||||
$returnType = new ArrayType(new StringType(), new MixedType(true));
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
|
||||
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,30 +191,13 @@ final class GetSubscriberEventsClassMethodFactory
|
|||
Expr $expr,
|
||||
Array_ $eventsToMethodsArray
|
||||
): void {
|
||||
|
||||
/** @var EventListenerTag[]|Tag[] $eventTags */
|
||||
$eventTags = $methodNamesWithPriorities[0]->getTags();
|
||||
foreach ($eventTags as $eventTag) {
|
||||
if ($eventTag instanceof EventListenerTag && $eventTag->getEvent() === $eventName) {
|
||||
$methodName = $eventTag->getMethod();
|
||||
$priority = $eventTag->getPriority();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($methodName, $priority)) {
|
||||
$methodName = $this->resolveMethodName($methodNamesWithPriorities[0], $eventName);
|
||||
$priority = $this->resolvePriority($methodNamesWithPriorities[0], $eventName);
|
||||
if ($methodName === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($priority !== 0) {
|
||||
$methodNameWithPriorityArray = new Array_();
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new String_($methodName));
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new LNumber((int) $priority));
|
||||
|
||||
$eventsToMethodsArray->items[] = new ArrayItem($methodNameWithPriorityArray, $expr);
|
||||
} else {
|
||||
$eventsToMethodsArray->items[] = new ArrayItem(new String_($methodName), $expr);
|
||||
}
|
||||
$eventsToMethodsArray->items[] = $this->createArrayItemFromMethodAndPriority($priority, $methodName, $expr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,25 +224,38 @@ final class GetSubscriberEventsClassMethodFactory
|
|||
}
|
||||
|
||||
$eventItems[] = $this->createEventItem($tag);
|
||||
|
||||
$alreadyUsedTags[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$multipleMethodsArray = new Array_($eventItems);
|
||||
|
||||
$eventsToMethodsArray->items[] = new ArrayItem($multipleMethodsArray, $expr);
|
||||
}
|
||||
|
||||
private function decorateClassMethodWithReturnType(ClassMethod $classMethod): void
|
||||
private function resolveMethodName(ServiceDefinition $serviceDefinition, string $eventName): ?string
|
||||
{
|
||||
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
|
||||
$classMethod->returnType = new Identifier('array');
|
||||
/** @var EventListenerTag[]|Tag[] $eventTags */
|
||||
$eventTags = $serviceDefinition->getTags();
|
||||
foreach ($eventTags as $eventTag) {
|
||||
if ($eventTag instanceof EventListenerTag && $eventTag->getEvent() === $eventName) {
|
||||
return $eventTag->getMethod();
|
||||
}
|
||||
}
|
||||
|
||||
$returnType = new ArrayType(new MixedType(), new MixedType(true));
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
|
||||
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $returnType);
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolvePriority(ServiceDefinition $serviceDefinition, string $eventName): ?int
|
||||
{
|
||||
/** @var EventListenerTag[]|Tag[] $eventTags */
|
||||
$eventTags = $serviceDefinition->getTags();
|
||||
foreach ($eventTags as $eventTag) {
|
||||
if ($eventTag instanceof EventListenerTag && $eventTag->getEvent() === $eventName) {
|
||||
return $eventTag->getPriority();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
|
@ -12,7 +12,7 @@ use PhpParser\Node\Stmt\Class_;
|
|||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Symfony\ValueObject\ServiceDefinition;
|
||||
use Rector\SymfonyCodeQuality\ApplicationMetadata\ListenerServiceDefinitionProvider;
|
||||
use Rector\SymfonyCodeQuality\NodeFactory\GetSubscriberEventsClassMethodFactory;
|
||||
use Rector\SymfonyCodeQuality\NodeFactory\GetSubscribedEventsClassMethodFactory;
|
||||
use Rector\SymfonyCodeQuality\ValueObject\EventNameToClassAndConstant;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
@ -54,13 +54,13 @@ final class EventListenerToEventSubscriberRector extends AbstractRector
|
|||
private $listenerServiceDefinitionProvider;
|
||||
|
||||
/**
|
||||
* @var GetSubscriberEventsClassMethodFactory
|
||||
* @var GetSubscribedEventsClassMethodFactory
|
||||
*/
|
||||
private $getSubscriberEventsClassMethodFactory;
|
||||
private $getSubscribedEventsClassMethodFactory;
|
||||
|
||||
public function __construct(
|
||||
ListenerServiceDefinitionProvider $listenerServiceDefinitionProvider,
|
||||
GetSubscriberEventsClassMethodFactory $getSubscriberEventsClassMethodFactory
|
||||
GetSubscribedEventsClassMethodFactory $getSubscribedEventsClassMethodFactory
|
||||
) {
|
||||
$this->eventNamesToClassConstants = [
|
||||
// kernel events
|
||||
|
@ -82,7 +82,7 @@ final class EventListenerToEventSubscriberRector extends AbstractRector
|
|||
new EventNameToClassAndConstant('console.error', self::CONSOLE_EVENTS_CLASS, 'ERROR'),
|
||||
];
|
||||
$this->listenerServiceDefinitionProvider = $listenerServiceDefinitionProvider;
|
||||
$this->getSubscriberEventsClassMethodFactory = $getSubscriberEventsClassMethodFactory;
|
||||
$this->getSubscribedEventsClassMethodFactory = $getSubscribedEventsClassMethodFactory;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
|
@ -191,7 +191,7 @@ CODE_SAMPLE
|
|||
|
||||
$class->name = new Identifier($classShortName . 'EventSubscriber');
|
||||
|
||||
$classMethod = $this->getSubscriberEventsClassMethodFactory->createFromEventsToMethods(
|
||||
$classMethod = $this->getSubscribedEventsClassMethodFactory->createFromServiceDefinitionsAndEventsToMethods(
|
||||
$eventsToMethods,
|
||||
$this->eventNamesToClassConstants
|
||||
);
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\ValueObject;
|
||||
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
|
||||
final class EventReferenceToMethodName
|
||||
{
|
||||
/**
|
||||
* @var ClassConstFetch
|
||||
*/
|
||||
private $classConstFetch;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $methodName;
|
||||
|
||||
public function __construct(ClassConstFetch $classConstFetch, string $methodName)
|
||||
{
|
||||
$this->classConstFetch = $classConstFetch;
|
||||
$this->methodName = $methodName;
|
||||
}
|
||||
|
||||
public function getClassConstFetch(): ClassConstFetch
|
||||
{
|
||||
return $this->classConstFetch;
|
||||
}
|
||||
|
||||
public function getMethodName(): string
|
||||
{
|
||||
return $this->methodName;
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ class MultipleMethodsEventSubscriber implements \Symfony\Component\EventDispatch
|
|||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ class MultipleListenersCalledOnceEventSubscriber implements \Symfony\Component\E
|
|||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ class MultipleMethodsWithNonKernelEventSubscriber implements \Symfony\Component\
|
|||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ class SomeEventSubscriber implements \Symfony\Component\EventDispatcher\EventSub
|
|||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ class WithPriorityEventSubscriber implements \Symfony\Component\EventDispatcher\
|
|||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
|
|
17
rules/symfony5/config/config.php
Normal file
17
rules/symfony5/config/config.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->defaults()
|
||||
->public()
|
||||
->autowire()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\Symfony5\\', __DIR__ . '/../src')
|
||||
->exclude([__DIR__ . '/../src/Rector']);
|
||||
};
|
120
rules/symfony5/src/NodeFactory/OnLogoutClassMethodFactory.php
Normal file
120
rules/symfony5/src/NodeFactory/OnLogoutClassMethodFactory.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony5\NodeFactory;
|
||||
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use Rector\Core\Php\PhpVersionProvider;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\NetteKdyby\NodeManipulator\ListeningClassMethodArgumentManipulator;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class OnLogoutClassMethodFactory
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const PARAMETER_TO_GETTER_NAMES = [
|
||||
'request' => 'getRequest',
|
||||
'response' => 'getResponse',
|
||||
'token' => 'getToken',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var NodeFactory
|
||||
*/
|
||||
private $nodeFactory;
|
||||
|
||||
/**
|
||||
* @var PhpVersionProvider
|
||||
*/
|
||||
private $phpVersionProvider;
|
||||
|
||||
/**
|
||||
* @var ListeningClassMethodArgumentManipulator
|
||||
*/
|
||||
private $listeningClassMethodArgumentManipulator;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(
|
||||
NodeFactory $nodeFactory,
|
||||
PhpVersionProvider $phpVersionProvider,
|
||||
ListeningClassMethodArgumentManipulator $listeningClassMethodArgumentManipulator,
|
||||
NodeNameResolver $nodeNameResolver
|
||||
) {
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
$this->listeningClassMethodArgumentManipulator = $listeningClassMethodArgumentManipulator;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
public function createFromLogoutClassMethod(ClassMethod $logoutClassMethod): ClassMethod
|
||||
{
|
||||
$classMethod = $this->nodeFactory->createPublicMethod('onLogout');
|
||||
|
||||
$logoutEventVariable = new Variable('logoutEvent');
|
||||
$classMethod->params[] = $this->createLogoutEventParam($logoutEventVariable);
|
||||
|
||||
$usedParams = [];
|
||||
foreach ($logoutClassMethod->params as $oldParam) {
|
||||
if (! $this->listeningClassMethodArgumentManipulator->isParamUsedInClassMethodBody(
|
||||
$logoutClassMethod,
|
||||
$oldParam
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$usedParams[] = $oldParam;
|
||||
}
|
||||
|
||||
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::VOID_TYPE)) {
|
||||
$classMethod->returnType = new Identifier('void');
|
||||
}
|
||||
|
||||
$assignStmts = $this->createAssignStmts($usedParams, $logoutEventVariable);
|
||||
$classMethod->stmts = array_merge($assignStmts, (array) $logoutClassMethod->stmts);
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
private function createLogoutEventParam(Variable $logoutEventVariable): Param
|
||||
{
|
||||
$param = new Param($logoutEventVariable);
|
||||
$param->type = new FullyQualified('Symfony\Component\Security\Http\Event\LogoutEvent');
|
||||
return $param;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Param[] $params
|
||||
* @return Expression[]
|
||||
*/
|
||||
private function createAssignStmts(array $params, Variable $logoutEventVariable): array
|
||||
{
|
||||
$assignStmts = [];
|
||||
foreach ($params as $param) {
|
||||
foreach (self::PARAMETER_TO_GETTER_NAMES as $parameterName => $getterName) {
|
||||
if (! $this->nodeNameResolver->isName($param, $parameterName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$assign = new Assign($param->var, new MethodCall($logoutEventVariable, $getterName));
|
||||
$assignStmts[] = new Expression($assign);
|
||||
}
|
||||
}
|
||||
|
||||
return $assignStmts;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony5\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Symfony5\NodeFactory\OnLogoutClassMethodFactory;
|
||||
use Rector\SymfonyCodeQuality\NodeFactory\GetSubscribedEventsClassMethodFactory;
|
||||
use Rector\SymfonyCodeQuality\ValueObject\EventReferenceToMethodName;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see https://github.com/symfony/symfony/pull/36243
|
||||
*
|
||||
* @see \Rector\Symfony5\Tests\Rector\Class_\LogoutHandlerToLogoutEventSubscriberRector\LogoutHandlerToLogoutEventSubscriberRectorTest
|
||||
*/
|
||||
final class LogoutHandlerToLogoutEventSubscriberRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const LOGOUT_HANDLER_TYPE = 'Symfony\Component\Security\Http\Logout\LogoutHandlerInterface';
|
||||
|
||||
/**
|
||||
* @var OnLogoutClassMethodFactory
|
||||
*/
|
||||
private $onLogoutClassMethodFactory;
|
||||
|
||||
/**
|
||||
* @var GetSubscribedEventsClassMethodFactory
|
||||
*/
|
||||
private $getSubscribedEventsClassMethodFactory;
|
||||
|
||||
public function __construct(
|
||||
OnLogoutClassMethodFactory $onLogoutClassMethodFactory,
|
||||
GetSubscribedEventsClassMethodFactory $getSubscribedEventsClassMethodFactory
|
||||
) {
|
||||
$this->onLogoutClassMethodFactory = $onLogoutClassMethodFactory;
|
||||
$this->getSubscribedEventsClassMethodFactory = $getSubscribedEventsClassMethodFactory;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change logout handler to an event listener that listens to LogoutEent', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
final class SomeLogoutHandler implements LogoutHandlerInterface
|
||||
{
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Http\Event\LogoutEvent;
|
||||
|
||||
final class SomeLogoutHandler implements EventSubscriberInterface
|
||||
{
|
||||
public function onLogout(LogoutEvent $logoutEvent): void
|
||||
{
|
||||
$request = $logoutEvent->getRequest();
|
||||
$response = $logoutEvent->getResponse();
|
||||
$token = $logoutEvent->getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string[]>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
LogoutEvent::class => ['onLogout'],
|
||||
];
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isObjectType($node, self::LOGOUT_HANDLER_TYPE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->refactorImplements($node);
|
||||
|
||||
// 2. refactor logout() class method to onLogout()
|
||||
$logoutClassMethod = $node->getMethod('logout');
|
||||
if (! $logoutClassMethod instanceof ClassMethod) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->stmts[] = $this->onLogoutClassMethodFactory->createFromLogoutClassMethod($logoutClassMethod);
|
||||
$this->removeNode($logoutClassMethod);
|
||||
|
||||
// 3. add getSubscribedEvents() class method
|
||||
$classConstFetch = $this->createClassConstReference('Symfony\Component\Security\Http\Event\LogoutEvent');
|
||||
|
||||
$eventReferencesToMethodNames = [new EventReferenceToMethodName($classConstFetch, 'onLogout')];
|
||||
$getSubscribedEventsClassMethod = $this->getSubscribedEventsClassMethodFactory->create(
|
||||
$eventReferencesToMethodNames
|
||||
);
|
||||
$node->stmts[] = $getSubscribedEventsClassMethod;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function refactorImplements(Class_ $class): void
|
||||
{
|
||||
$class->implements[] = new FullyQualified('Symfony\Component\EventDispatcher\EventSubscriberInterface');
|
||||
|
||||
foreach ($class->implements as $key => $implement) {
|
||||
if (! $this->isName($implement, self::LOGOUT_HANDLER_TYPE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($class->implements[$key]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -108,7 +108,7 @@ CODE_SAMPLE
|
|||
|
||||
private function shouldSkip(MethodCall $methodCall): bool
|
||||
{
|
||||
if (! $this->isObjectType($methodCall, 'Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor')) {
|
||||
if (! $this->isObjectType($methodCall->var, 'Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -155,16 +155,17 @@ CODE_SAMPLE
|
|||
|
||||
private function prepareEnableMagicMethodsExtractionFlags(bool $enableMagicCallExtractionValue): BitwiseOr
|
||||
{
|
||||
$classConstFetch = $this->createClassConstFetch(
|
||||
$magicGetClassConstFetch = $this->createClassConstFetch(
|
||||
'Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor',
|
||||
'MAGIC_GET'
|
||||
);
|
||||
$magicSet = $this->createClassConstFetch(
|
||||
$magicSetClassConstFetch = $this->createClassConstFetch(
|
||||
'Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor',
|
||||
'MAGIC_SET'
|
||||
);
|
||||
|
||||
if (! $enableMagicCallExtractionValue) {
|
||||
return new BitwiseOr($classConstFetch, $magicSet);
|
||||
return new BitwiseOr($magicGetClassConstFetch, $magicSetClassConstFetch);
|
||||
}
|
||||
|
||||
return new BitwiseOr(
|
||||
|
@ -173,9 +174,9 @@ CODE_SAMPLE
|
|||
'Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor',
|
||||
'MAGIC_CALL'
|
||||
),
|
||||
$classConstFetch,
|
||||
$magicGetClassConstFetch,
|
||||
),
|
||||
$magicSet,
|
||||
$magicSetClassConstFetch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Symfony5\Tests\Rector\Class_\LogoutHandlerToLogoutEventSubscriberRector\Fixture;
|
||||
|
||||
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
final class SomeLogoutHandler implements LogoutHandlerInterface
|
||||
{
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
$result = $request;
|
||||
$nextResult = $token;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Symfony5\Tests\Rector\Class_\LogoutHandlerToLogoutEventSubscriberRector\Fixture;
|
||||
|
||||
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
final class SomeLogoutHandler implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function onLogout(\Symfony\Component\Security\Http\Event\LogoutEvent $logoutEvent): void
|
||||
{
|
||||
$request = $logoutEvent->getRequest();
|
||||
$token = $logoutEvent->getToken();
|
||||
$result = $request;
|
||||
$nextResult = $token;
|
||||
}
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [\Symfony\Component\Security\Http\Event\LogoutEvent::class => 'onLogout'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony5\Tests\Rector\Class_\LogoutHandlerToLogoutEventSubscriberRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Symfony5\Rector\Class_\LogoutHandlerToLogoutEventSubscriberRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class LogoutHandlerToLogoutEventSubscriberRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return LogoutHandlerToLogoutEventSubscriberRector::class;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user