Merge pull request #2792 from rectorphp/cake-route

decopule ImplicitToExplicitRoutingAnnotationDecorator
This commit is contained in:
Tomas Votruba 2020-02-02 12:41:27 +01:00 committed by GitHub
commit 29c8de530b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 55 deletions

View File

@ -6,11 +6,10 @@ namespace Rector\CakePHPToSymfony\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\CakePHPToSymfony\Rector\AbstractCakePHPRector;
use Rector\FrameworkMigration\Symfony\ImplicitToExplicitRoutingAnnotationDecorator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Rector\Util\RectorStrings;
@ -24,9 +23,15 @@ use Rector\Util\RectorStrings;
final class CakePHPImplicitRouteToExplicitRouteAnnotationRector extends AbstractCakePHPRector
{
/**
* @var string
* @var ImplicitToExplicitRoutingAnnotationDecorator
*/
private const HAS_FRESH_ROUTE_ANNOTATION_ATTRIBUTE = 'has_fresh_route_annotation';
private $implicitToExplicitRoutingAnnotationDecorator;
public function __construct(
ImplicitToExplicitRoutingAnnotationDecorator $implicitToExplicitRoutingAnnotationDecorator
) {
$this->implicitToExplicitRoutingAnnotationDecorator = $implicitToExplicitRoutingAnnotationDecorator;
}
public function getDefinition(): RectorDefinition
{
@ -92,7 +97,11 @@ PHP
$name = RectorStrings::camelCaseToUnderscore($combined);
$symfonyRoutePhpDocTagValueNode = $this->createSymfonyRoutePhpDocTagValueNode($path, $name);
$this->addSymfonyRouteShortTagNodeWithUse($symfonyRoutePhpDocTagValueNode, $classMethod);
$this->implicitToExplicitRoutingAnnotationDecorator->decorateClassMethodWithRouteAnnotation(
$classMethod,
$symfonyRoutePhpDocTagValueNode
);
}
return $node;
@ -102,20 +111,4 @@ PHP
{
return new SymfonyRouteTagValueNode($path, $name);
}
/**
* @todo reuse from RouterListToControllerAnnotationsRector
*/
private function addSymfonyRouteShortTagNodeWithUse(
SymfonyRouteTagValueNode $symfonyRouteTagValueNode,
ClassMethod $classMethod
): void {
// @todo use empty phpdoc info
$this->docBlockManipulator->addTagValueNodeWithShortName($classMethod, $symfonyRouteTagValueNode);
$symfonyRouteUseObjectType = new FullyQualifiedObjectType(SymfonyRouteTagValueNode::CLASS_NAME);
$this->addUseType($symfonyRouteUseObjectType, $classMethod);
$classMethod->setAttribute(self::HAS_FRESH_ROUTE_ANNOTATION_ATTRIBUTE, true);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPImplicitRouteToExplicitRouteAnnotationRector\Fixture;
class SkipPrivateController extends \AppController
{
private function index()
{
}
}

View File

@ -0,0 +1,10 @@
parameters:
project_directory: null
services:
_defaults:
public: true
autowire: true
Rector\FrameworkMigration\:
resource: '../src'

View File

@ -4,7 +4,60 @@ declare(strict_types=1);
namespace Rector\FrameworkMigration\Symfony;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
final class ImplicitToExplicitRoutingAnnotationDecorator
{
// ...
/**
* @var string
*/
public const HAS_ROUTE_ANNOTATION = 'has_route_annotation';
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
/**
* @var UseAddingCommander
*/
private $useAddingCommander;
public function __construct(DocBlockManipulator $docBlockManipulator, UseAddingCommander $useAddingCommander)
{
$this->docBlockManipulator = $docBlockManipulator;
$this->useAddingCommander = $useAddingCommander;
}
public function decorateClassMethodWithRouteAnnotation(
ClassMethod $classMethod,
SymfonyRouteTagValueNode $symfonyRouteTagValueNode
): void {
$this->docBlockManipulator->addTagValueNodeWithShortName($classMethod, $symfonyRouteTagValueNode);
$symfonyRouteUseObjectType = new FullyQualifiedObjectType(SymfonyRouteTagValueNode::CLASS_NAME);
$this->addUseType($symfonyRouteUseObjectType, $classMethod);
// remove
$this->useAddingCommander->removeShortUse($classMethod, 'Route');
$classMethod->setAttribute(self::HAS_ROUTE_ANNOTATION, true);
}
/**
* @param FullyQualifiedObjectType|AliasedObjectType $objectType
*/
private function addUseType(ObjectType $objectType, Node $positionNode): void
{
assert($objectType instanceof FullyQualifiedObjectType || $objectType instanceof AliasedObjectType);
$this->useAddingCommander->addUseImport($positionNode, $objectType);
}
}

View File

@ -15,11 +15,11 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\FrameworkMigration\Symfony\ImplicitToExplicitRoutingAnnotationDecorator;
use Rector\NetteToSymfony\Route\RouteInfoFactory;
use Rector\NetteToSymfony\ValueObject\RouteInfo;
use Rector\NodeContainer\ParsedNodesByType;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -35,11 +35,6 @@ use ReflectionMethod;
*/
final class RouterListToControllerAnnotationsRector extends AbstractRector
{
/**
* @var string
*/
private const HAS_FRESH_ROUTE_ANNOTATION_ATTRIBUTE = 'has_fresh_route_annotation';
/**
* @var ParsedNodesByType
*/
@ -55,14 +50,21 @@ final class RouterListToControllerAnnotationsRector extends AbstractRector
*/
private $returnTypeInferer;
/**
* @var ImplicitToExplicitRoutingAnnotationDecorator
*/
private $implicitToExplicitRoutingAnnotationDecorator;
public function __construct(
ParsedNodesByType $parsedNodesByType,
RouteInfoFactory $routeInfoFactory,
ReturnTypeInferer $returnTypeInferer
ReturnTypeInferer $returnTypeInferer,
ImplicitToExplicitRoutingAnnotationDecorator $implicitToExplicitRoutingAnnotationDecorator
) {
$this->parsedNodesByType = $parsedNodesByType;
$this->routeInfoFactory = $routeInfoFactory;
$this->returnTypeInferer = $returnTypeInferer;
$this->implicitToExplicitRoutingAnnotationDecorator = $implicitToExplicitRoutingAnnotationDecorator;
}
public function getDefinition(): RectorDefinition
@ -163,7 +165,10 @@ PHP
$symfonyRoutePhpDocTagValueNode = $this->createSymfonyRoutePhpDocTagValueNode($routeInfo);
$this->addSymfonyRouteShortTagNodeWithUse($symfonyRoutePhpDocTagValueNode, $classMethod);
$this->implicitToExplicitRoutingAnnotationDecorator->decorateClassMethodWithRouteAnnotation(
$classMethod,
$symfonyRoutePhpDocTagValueNode
);
}
// complete all other non-explicit methods, from "<presenter>/<action>"
@ -240,21 +245,6 @@ PHP
return new SymfonyRouteTagValueNode($routeInfo->getPath(), null, $routeInfo->getHttpMethods());
}
private function addSymfonyRouteShortTagNodeWithUse(
SymfonyRouteTagValueNode $symfonyRouteTagValueNode,
ClassMethod $classMethod
): void {
$this->docBlockManipulator->addTagValueNodeWithShortName($classMethod, $symfonyRouteTagValueNode);
$symfonyRouteUseObjectType = new FullyQualifiedObjectType(SymfonyRouteTagValueNode::CLASS_NAME);
$this->addUseType($symfonyRouteUseObjectType, $classMethod);
// remove
$this->removeShortUse('Route', $classMethod);
$classMethod->setAttribute(self::HAS_FRESH_ROUTE_ANNOTATION_ATTRIBUTE, true);
}
private function completeImplicitRoutes(): void
{
$presenterClasses = $this->classLikeParsedNodesFinder->findClassesBySuffix('Presenter');
@ -268,14 +258,14 @@ PHP
$path = $this->resolvePathFromClassAndMethodNodes($presenterClass, $classMethod);
$symfonyRoutePhpDocTagValueNode = new SymfonyRouteTagValueNode($path);
$this->addSymfonyRouteShortTagNodeWithUse($symfonyRoutePhpDocTagValueNode, $classMethod);
$this->implicitToExplicitRoutingAnnotationDecorator->decorateClassMethodWithRouteAnnotation(
$classMethod,
$symfonyRoutePhpDocTagValueNode
);
}
}
}
/**
* @todo allow extension with custom resolvers
*/
private function isRouteStaticCallMatch(StaticCall $staticCall): bool
{
$className = $this->getName($staticCall->class);
@ -319,7 +309,7 @@ PHP
return true;
}
if ($node->getAttribute(self::HAS_FRESH_ROUTE_ANNOTATION_ATTRIBUTE)) {
if ($node->getAttribute(ImplicitToExplicitRoutingAnnotationDecorator::HAS_ROUTE_ANNOTATION)) {
return true;
}

View File

@ -87,11 +87,6 @@ trait NodeCommandersTrait
$this->useAddingCommander->addUseImport($positionNode, $objectType);
}
protected function removeShortUse(string $shortUse, Node $positionNode): void
{
$this->useAddingCommander->removeShortUse($positionNode, $shortUse);
}
protected function addNodeAfterNode(Node $newNode, Node $positionNode): void
{
$this->nodeAddingCommander->addNodeAfterNode($newNode, $positionNode);