Skip to content

Commit

Permalink
[Symfony 5.2] Add LogoutHandlerToLogoutEventSubscriberRector
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Jan 28, 2021
1 parent f515c8e commit 0580c60
Show file tree
Hide file tree
Showing 14 changed files with 574 additions and 78 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion config/set/symfony51.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,6 @@
// @see https://github.com/symfony/symfony/pull/35858
RenameStringRector::STRING_CHANGES => [
'ROLE_PREVIOUS_ADMIN' => 'IS_IMPERSONATOR',
]
],
]]);
};
2 changes: 1 addition & 1 deletion packages/rector-generator/src/TemplateVariablesFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private function createCodeForDefinition(string $code): string
{
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,11 @@ function (string $className) use ($interfaceName): bool {
);
}

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,7 @@ public function change(array $classMethodsByEventClass, ?EventAndListenerTree $e
}
}

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
Expand All @@ -138,6 +129,15 @@ private function isParamUsedInClassMethodBody(ClassMethod $classMethod, Param $p
});
}

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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Rector\SymfonyCodeQuality\NodeFactory;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
Expand All @@ -16,6 +15,7 @@
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;
Expand All @@ -27,9 +27,15 @@
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
*/
Expand All @@ -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 array<string, ServiceDefinition[]> $eventsToMethods
* @param EventNameToClassAndConstant[] $eventNamesToClassConstants
* @param EventReferenceToMethodName[] $eventReferencesToMethodNames
*/
public function createFromEventsToMethods(array $eventsToMethods, array $eventNamesToClassConstants): ClassMethod
public function create(array $eventReferencesToMethodNames): ClassMethod
{
$getSubscribersClassMethod = $this->nodeFactory->createPublicMethod('getSubscribedEvents');
$getSubscribersClassMethod = $this->createClassMethod();

$eventsToMethodsArray = new Array_();

$this->visibilityManipulator->makeStatic($getSubscribersClassMethod);
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 createFromServiceDefinitionsAndEventsToMethods(
array $eventsToMethods,
array $eventNamesToClassConstants
): ClassMethod {
$getSubscribersClassMethod = $this->createClassMethod();

$eventsToMethodsArray = new Array_();

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(
Expand All @@ -107,31 +149,6 @@ public function createFromEventsToMethods(array $eventsToMethods, array $eventNa
return $getSubscribersClassMethod;
}

/**
* @param EventNameToClassAndConstant[] $eventNamesToClassConstants
* @return String_|ClassConstFetch
*/
private 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);
}

/**
* @param ClassConstFetch|String_ $expr
* @param ServiceDefinition[] $methodNamesWithPriorities
Expand All @@ -142,30 +159,13 @@ private function createSingleMethod(
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);
}

/**
Expand All @@ -192,13 +192,11 @@ private function createMultipleMethods(
}

$eventItems[] = $this->createEventItem($tag);

$alreadyUsedTags[] = $tag;
}
}

$multipleMethodsArray = new Array_($eventItems);

$eventsToMethodsArray->items[] = new ArrayItem($multipleMethodsArray, $expr);
}

Expand All @@ -208,7 +206,7 @@ private function decorateClassMethodWithReturnType(ClassMethod $classMethod): vo
$classMethod->returnType = new Identifier('array');
}

$returnType = new ArrayType(new MixedType(), new MixedType(true));
$returnType = new ArrayType(new StringType(), new MixedType(true));
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $returnType);
}
Expand Down Expand Up @@ -237,4 +235,51 @@ private function createEventItem(EventListenerTag $eventListenerTag): ArrayItem

return new ArrayItem(new String_($eventListenerTag->getMethod()));
}

private function resolveMethodName(ServiceDefinition $serviceDefinition, string $eventName): ?string
{
/** @var EventListenerTag[]|Tag[] $eventTags */
$eventTags = $serviceDefinition->getTags();
foreach ($eventTags as $eventTag) {
if ($eventTag instanceof EventListenerTag && $eventTag->getEvent() === $eventName) {
return $eventTag->getMethod();
}
}

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;
}

private function createClassMethod(): ClassMethod
{
$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);
}

return new ArrayItem(new String_($methodName), $expr);
}
}
Loading

0 comments on commit 0580c60

Please sign in to comment.