[Nette Kdyby] Add ReplaceEventManagerWithEventSubscriberRector

This commit is contained in:
TomasVotruba 2020-05-28 21:20:16 +02:00
parent d40cdeafc6
commit f0a67edb7e
12 changed files with 284 additions and 35 deletions

View File

@ -4,3 +4,4 @@ services:
Rector\NetteKdyby\Rector\Class_\KdybyEventSubscriberToContributteEventSubscriberRector: null
Rector\NetteKdyby\Rector\MethodCall\ReplaceMagicPropertyEventWithEventClassRector: null
Rector\NetteKdyby\Rector\ClassMethod\ReplaceMagicEventPropertySubscriberWithEventClassSubscriberRector: null
Rector\NetteKdyby\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector: null

View File

@ -309,3 +309,4 @@ parameters:
- '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\Type\\ObjectTypeSpecifier\:\:matchShortenedObjectType\(\)" is 10, keep it under 9#'
- '#Parameter \#1 \$objectType of method Rector\\Core\\Naming\\PropertyNaming\:\:fqnToVariableName\(\) expects PHPStan\\Type\\ObjectType\|string, PHPStan\\Type\\Type given#'
- '#Parameter \#1 \$type of method PhpParser\\Builder\\FunctionLike\:\:setReturnType\(\) expects PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|string, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#'
- '#Cognitive complexity for "Rector\\Core\\PhpParser\\Node\\Value\\ValueResolver\:\:getValue\(\)" is \d+, keep it under 9#'

View File

@ -251,7 +251,7 @@ PHP
string $name,
ObjectType $objectType
): void {
$assign = $this->createSameNameThisAssign($name);
$assign = $this->nodeFactory->createPropertyAssignment($name);
$classMethod->stmts[] = new Expression($assign);
$this->addPropertyToClass($class, $objectType, $name);
@ -296,16 +296,6 @@ PHP
return $this->isName($assign->expr->name, self::GET_MANAGER);
}
/**
* Creates: "$this->value = $value;"
*/
private function createSameNameThisAssign(string $name): Assign
{
$propertyFetch = new PropertyFetch(new Variable('this'), $name);
return new Assign($propertyFetch, new Variable($name));
}
private function removeManagerRegistryProperty(Class_ $class, Assign $assign): void
{
$managerRegistryPropertyName = $this->getName($assign->var);

View File

@ -54,12 +54,28 @@ final class EventClassNaming
return $fileInfo->getPath() . DIRECTORY_SEPARATOR . 'Event' . DIRECTORY_SEPARATOR . $shortEventClassName . '.php';
}
public function createEventClassNameFromClassAndProperty(string $className, string $methodName): string
{
$shortEventClass = $this->createShortEventClassNameFromClassAndProperty($className, $methodName);
return $this->prependShortClassEventWithNamespace($shortEventClass, $className);
}
public function createEventClassNameFromClassPropertyReference(string $classAndPropertyName): string
{
[$class, $property] = Strings::split($classAndPropertyName, '#::#');
$shortEventClass = $this->createShortEventClassNameFromClassAndProperty($class, $property);
return $this->prependShortClassEventWithNamespace($shortEventClass, $class);
}
/**
* TomatoMarket, onBuy
*
* TomatoMarketBuyEvent
*/
public function createShortEventClassNameFromClassAndProperty(string $class, string $property): string
private function createShortEventClassNameFromClassAndProperty(string $class, string $property): string
{
$shortClassName = $this->classNaming->getShortName($class);
@ -69,13 +85,6 @@ final class EventClassNaming
return $shortClassName . $shortPropertyName . 'Event';
}
public function createEventClassNameFromClassAndProperty(string $className, string $methodName): string
{
$shortEventClass = $this->createShortEventClassNameFromClassAndProperty($className, $methodName);
return $this->prependShortClassEventWithNamespace($shortEventClass, $className);
}
private function getShortEventClassName(MethodCall $methodCall): string
{
/** @var string $methodName */

View File

@ -159,14 +159,13 @@ PHP
return null;
}
[$class, $property] = Strings::split($eventPropertyReferenceName, '#::#');
if (! property_exists($class, $property)) {
$eventClassName = $this->eventClassNaming->createEventClassNameFromClassPropertyReference(
$eventPropertyReferenceName
);
if ($eventClassName === null) {
return null;
}
$eventClassName = $this->eventClassNaming->createEventClassNameFromClassAndProperty($class, $property);
$node->key = $this->createClassConstantReference($eventClassName);
});
}

View File

@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
namespace Rector\NetteKdyby\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NetteKdyby\Naming\EventClassNaming;
/**
* @see \Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector\ReplaceEventManagerWithEventSubscriberRectorTest
*/
final class ReplaceEventManagerWithEventSubscriberRector extends AbstractRector
{
/**
* @var EventClassNaming
*/
private $eventClassNaming;
public function __construct(EventClassNaming $eventClassNaming)
{
$this->eventClassNaming = $eventClassNaming;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change Kdyby EventManager to EventDispatcher', [
new CodeSample(
<<<'PHP'
use Kdyby\Events\EventManager;
final class SomeClass
{
/**
* @var EventManager
*/
private $eventManager;
public function __construct(EventManager $eventManager)
{
$this->eventManager = eventManager;
}
public function run()
{
$key = '2000';
$this->eventManager->dispatchEvent(static::class . '::onCopy', new EventArgsList([$this, $key]));
}
}
PHP
,
<<<'PHP'
use Kdyby\Events\EventManager;
final class SomeClass
{
/**
* @var EventManager
*/
private $eventManager;
public function __construct(EventManager $eventManager)
{
$this->eventManager = eventManager;
}
public function run()
{
$key = '2000';
$this->eventManager->dispatch(new SomeClassCopyEvent($this, $key));
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [MethodCall::class];
}
/**
* @param MethodCall $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node->var, 'Kdyby\Events\EventManager')) {
return null;
}
if (! $this->isName($node->name, 'dispatchEvent')) {
return null;
}
$node->name = new Identifier('dispatch');
$oldArgs = $node->args;
$node->args = [];
$eventReference = $oldArgs[0]->value;
$classAndStaticProperty = $this->getValue($eventReference, true);
$eventCass = $this->eventClassNaming->createEventClassNameFromClassPropertyReference($classAndStaticProperty);
$args = [];
if ($oldArgs[1]->value instanceof New_) {
/** @var New_ $new */
$new = $oldArgs[1]->value;
$array = $new->args[0]->value;
if ($array instanceof Array_) {
foreach ($array->items as $arrayItem) {
$args[] = new Arg($arrayItem->value);
}
}
}
$class = new New_(new FullyQualified($eventCass), $args);
$node->args[] = new Arg($class);
return $node;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector\Fixture;
use Kdyby\Events\EventManager;
final class SomeClass
{
/**
* @var EventManager
*/
private $eventManager;
public function __construct(EventManager $eventManager)
{
$this->eventManager = eventManager;
}
public function run()
{
$key = '2000';
$this->eventManager->dispatchEvent(static::class . '::onCopy', new EventArgsList([$this, $key]));
}
}
?>
-----
<?php
namespace Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector\Fixture;
use Kdyby\Events\EventManager;
final class SomeClass
{
/**
* @var EventManager
*/
private $eventManager;
public function __construct(EventManager $eventManager)
{
$this->eventManager = eventManager;
}
public function run()
{
$key = '2000';
$this->eventManager->dispatch(new \Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector\Fixture\Event\SomeClassCopyEvent($this, $key));
}
}
?>

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\NetteKdyby\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector;
final class ReplaceEventManagerWithEventSubscriberRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return ReplaceEventManagerWithEventSubscriberRector::class;
}
}

View File

@ -150,8 +150,7 @@ PHP
$paramBuilder->setType(new FullyQualified($staticType));
$param = $paramBuilder->getNode();
$propertyFetch = new PropertyFetch(new Variable('this'), $variableName);
$assign = new Assign($propertyFetch, new Variable($variableName));
$assign = $this->nodeFactory->createPropertyAssignment($variableName);
$setEntityFactoryMethod = $this->createSetEntityFactoryClassMethod($variableName, $param, $assign);

View File

@ -6,15 +6,13 @@ namespace Rector\SOLID\NodeFactory;
use PhpParser\Builder\Method;
use PhpParser\Builder\Param;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Core\Naming\PropertyNaming;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\SOLID\Rector\Class_\MultiParentingToAbstractDependencyRector;
@ -40,16 +38,23 @@ final class InjectMethodFactory
*/
private $typeFactory;
/**
* @var NodeFactory
*/
private $nodeFactory;
public function __construct(
PhpDocInfoFactory $phpDocInfoFactory,
PropertyNaming $propertyNaming,
ClassNaming $classNaming,
TypeFactory $typeFactory
TypeFactory $typeFactory,
NodeFactory $nodeFactory
) {
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->propertyNaming = $propertyNaming;
$this->classNaming = $classNaming;
$this->typeFactory = $typeFactory;
$this->nodeFactory = $nodeFactory;
}
/**
@ -72,8 +77,8 @@ final class InjectMethodFactory
$param->setType(new FullyQualified($objectType->getClassName()));
$methodBuilder->addParam($param);
$propertyFetch = new PropertyFetch(new Variable('this'), $propertyName);
$assign = new Assign($propertyFetch, new Variable($propertyName));
$assign = $this->nodeFactory->createPropertyAssignment($propertyName);
$methodBuilder->addStmt($assign);
}

View File

@ -7,6 +7,7 @@ namespace Rector\Core\PhpParser\Node\Value;
use PhpParser\ConstExprEvaluationException;
use PhpParser\ConstExprEvaluator;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Scalar\MagicConst\Dir;
@ -63,8 +64,24 @@ final class ValueResolver
/**
* @return mixed|null
*/
public function getValue(Expr $expr)
public function getValue(Expr $expr, bool $resolvedClassReference = false)
{
if ($expr instanceof Concat) {
return $this->processConcat($expr, $resolvedClassReference);
}
if ($expr instanceof ClassConstFetch && $resolvedClassReference) {
$class = $this->nodeNameResolver->getName($expr->class);
if (in_array($class, ['self', 'static'], true)) {
return $expr->getAttribute(AttributeKey::CLASS_NAME);
}
if ($this->nodeNameResolver->isName($expr->name, 'class')) {
return $class;
}
}
try {
$value = $this->getConstExprEvaluator()->evaluateDirectly($expr);
} catch (ConstExprEvaluationException $constExprEvaluationException) {
@ -201,4 +218,12 @@ final class ValueResolver
return $this->constExprEvaluator->evaluateDirectly($classConstNode->consts[0]->value);
}
private function processConcat($expr, bool $resolvedClassReference): string
{
return $this->getValue($expr->left, $resolvedClassReference) . $this->getValue(
$expr->right,
$resolvedClassReference
);
}
}

View File

@ -26,9 +26,9 @@ trait ValueResolverTrait
$this->valueResolver = $valueResolver;
}
protected function getValue(Expr $expr)
protected function getValue(Expr $expr, bool $resolvedClassReference = false)
{
return $this->valueResolver->getValue($expr);
return $this->valueResolver->getValue($expr, $resolvedClassReference);
}
protected function isValue(Expr $expr, $expectedValue): bool