diff --git a/config/set/nette/kdyby-events-to-contributte-event-dispatcher.yaml b/config/set/nette/kdyby-events-to-contributte-event-dispatcher.yaml index 452d32e0a677..8cb45057cc1b 100644 --- a/config/set/nette/kdyby-events-to-contributte-event-dispatcher.yaml +++ b/config/set/nette/kdyby-events-to-contributte-event-dispatcher.yaml @@ -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 diff --git a/phpstan.neon b/phpstan.neon index f5bd1e23991d..dc4a0831fac9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -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#' diff --git a/rules/doctrine/src/Rector/Class_/ManagerRegistryGetManagerToEntityManagerRector.php b/rules/doctrine/src/Rector/Class_/ManagerRegistryGetManagerToEntityManagerRector.php index 19fe55bbb200..22f956211ab8 100644 --- a/rules/doctrine/src/Rector/Class_/ManagerRegistryGetManagerToEntityManagerRector.php +++ b/rules/doctrine/src/Rector/Class_/ManagerRegistryGetManagerToEntityManagerRector.php @@ -251,7 +251,7 @@ private function addConstructorDependencyWithProperty( 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 @@ private function isRegistryGetManagerMethodCall(Assign $assign): bool 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); diff --git a/rules/nette-kdyby/src/Naming/EventClassNaming.php b/rules/nette-kdyby/src/Naming/EventClassNaming.php index f5dfbb328944..0580e7c80f31 100644 --- a/rules/nette-kdyby/src/Naming/EventClassNaming.php +++ b/rules/nette-kdyby/src/Naming/EventClassNaming.php @@ -54,12 +54,28 @@ public function resolveEventFileLocation(MethodCall $methodCall): string 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 @@ public function createShortEventClassNameFromClassAndProperty(string $class, str 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 */ diff --git a/rules/nette-kdyby/src/Rector/ClassMethod/ReplaceMagicEventPropertySubscriberWithEventClassSubscriberRector.php b/rules/nette-kdyby/src/Rector/ClassMethod/ReplaceMagicEventPropertySubscriberWithEventClassSubscriberRector.php index 1a6a998d0a20..2317f8da4331 100644 --- a/rules/nette-kdyby/src/Rector/ClassMethod/ReplaceMagicEventPropertySubscriberWithEventClassSubscriberRector.php +++ b/rules/nette-kdyby/src/Rector/ClassMethod/ReplaceMagicEventPropertySubscriberWithEventClassSubscriberRector.php @@ -159,14 +159,13 @@ private function replaceEventPropertyReferenceWithEventClassReference(ClassMetho 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); }); } diff --git a/rules/nette-kdyby/src/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector.php b/rules/nette-kdyby/src/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector.php new file mode 100644 index 000000000000..b9658f506d3f --- /dev/null +++ b/rules/nette-kdyby/src/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector.php @@ -0,0 +1,137 @@ +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; + } +} diff --git a/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Fixture/fixture.php.inc b/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Fixture/fixture.php.inc new file mode 100644 index 000000000000..4f88faec0314 --- /dev/null +++ b/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Fixture/fixture.php.inc @@ -0,0 +1,53 @@ +eventManager = eventManager; + } + + public function run() + { + $key = '2000'; + $this->eventManager->dispatchEvent(static::class . '::onCopy', new EventArgsList([$this, $key])); + } +} + +?> +----- +eventManager = eventManager; + } + + public function run() + { + $key = '2000'; + $this->eventManager->dispatch(new \Rector\NetteKdyby\Tests\Rector\MethodCall\ReplaceEventManagerWithEventSubscriberRector\Fixture\Event\SomeClassCopyEvent($this, $key)); + } +} + +?> diff --git a/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/ReplaceEventManagerWithEventSubscriberRectorTest.php b/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/ReplaceEventManagerWithEventSubscriberRectorTest.php new file mode 100644 index 000000000000..cb79454cb9fb --- /dev/null +++ b/rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/ReplaceEventManagerWithEventSubscriberRectorTest.php @@ -0,0 +1,30 @@ +doTestFile($file); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + protected function getRectorClass(): string + { + return ReplaceEventManagerWithEventSubscriberRector::class; + } +} diff --git a/rules/removing-static/src/Rector/Class_/StaticTypeToSetterInjectionRector.php b/rules/removing-static/src/Rector/Class_/StaticTypeToSetterInjectionRector.php index e7d86f5d2a40..557c4f862c6a 100644 --- a/rules/removing-static/src/Rector/Class_/StaticTypeToSetterInjectionRector.php +++ b/rules/removing-static/src/Rector/Class_/StaticTypeToSetterInjectionRector.php @@ -150,8 +150,7 @@ function (Node $node) use ($objectType): bool { $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); diff --git a/rules/solid/src/NodeFactory/InjectMethodFactory.php b/rules/solid/src/NodeFactory/InjectMethodFactory.php index 626038127445..0d12262c9d53 100644 --- a/rules/solid/src/NodeFactory/InjectMethodFactory.php +++ b/rules/solid/src/NodeFactory/InjectMethodFactory.php @@ -6,15 +6,13 @@ 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 @@ public function createFromTypes(array $objectTypes, string $className, string $f $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); } diff --git a/src/PhpParser/Node/Value/ValueResolver.php b/src/PhpParser/Node/Value/ValueResolver.php index b76ee4270d2f..2766f1e7c54e 100644 --- a/src/PhpParser/Node/Value/ValueResolver.php +++ b/src/PhpParser/Node/Value/ValueResolver.php @@ -7,6 +7,7 @@ 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 @@ public function isValue(Expr $expr, $value): bool /** * @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 @@ private function resolveClassConstFetch(ClassConstFetch $classConstFetch) 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 + ); + } } diff --git a/src/Rector/AbstractRector/ValueResolverTrait.php b/src/Rector/AbstractRector/ValueResolverTrait.php index ae881b2a4e1f..7de7648a69d4 100644 --- a/src/Rector/AbstractRector/ValueResolverTrait.php +++ b/src/Rector/AbstractRector/ValueResolverTrait.php @@ -26,9 +26,9 @@ public function autowireValueResolverTrait(ValueResolver $valueResolver): void $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